[Scummvm-git-logs] scummvm master -> e51035f2e79f21ab2b8d1c3536b4c9c2986c7f62

dreammaster noreply at scummvm.org
Thu May 28 11:13:44 UTC 2026


This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
17751dd9b7 MADS: DRAGONSPHERE: Fix array indexing error in map screen
acd8198462 MADS: DRAGONSPHERE: Implement custom player_keep_walking for the game
7175f1472e MADS: FOREST: Skeleton subengine codebase
74ed86e494 MADS: FOREST: Implementing section 9 rooms
a57e2b6ae1 MADS: DRAGONSPHERE: Warning fixes
e51035f2e7 MADS: DRAGONSPHERE: Fix re-entering Caliph's tent


Commit: 17751dd9b71b9b2f830fb92f3eba8171e1fd1214
    https://github.com/scummvm/scummvm/commit/17751dd9b71b9b2f830fb92f3eba8171e1fd1214
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-28T21:12:18+10:00

Commit Message:
MADS: DRAGONSPHERE: Fix array indexing error in map screen

Changed paths:
    engines/mads/madsv2/core/player.cpp


diff --git a/engines/mads/madsv2/core/player.cpp b/engines/mads/madsv2/core/player.cpp
index 41aeccd3403..04dc8e1714c 100644
--- a/engines/mads/madsv2/core/player.cpp
+++ b/engines/mads/madsv2/core/player.cpp
@@ -797,6 +797,7 @@ static void player_walk_directly(int walk_form) {
 void player_new_command() {
 	int count;
 	int walk_spot;
+	int id;
 
 	player_cancel_command();
 
@@ -866,10 +867,11 @@ void player_new_command() {
 
 	// Be sure player moves immediately even if in the middle of a long
 	// stop-walker frame.
-	// WORKAROUND: For ROTP chandelier fight cutscene
-	if (!series_list[player.series_base + player.series])
+	// WORKAROUND: For ROTP chandelier fight cutscene and Dragonsphere cutscenes
+	id = player.series_base + player.series;
+	if (id < 0 || !series_list[id])
 		return;
-	WalkerInfoPtr walker = series_list[player.series_base + player.series]->walker;
+	WalkerInfoPtr walker = series_list[id]->walker;
 	if (!walker)
 		return;
 


Commit: acd8198462b347b429c248b147ef41521d857667
    https://github.com/scummvm/scummvm/commit/acd8198462b347b429c248b147ef41521d857667
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-28T21:12:19+10:00

Commit Message:
MADS: DRAGONSPHERE: Implement custom player_keep_walking for the game

Changed paths:
    engines/mads/madsv2/core/player.cpp
    engines/mads/madsv2/core/player.h
    engines/mads/madsv2/dragonsphere/dragonsphere.cpp
    engines/mads/madsv2/dragonsphere/dragonsphere.h
    engines/mads/madsv2/engine.cpp
    engines/mads/madsv2/engine.h


diff --git a/engines/mads/madsv2/core/player.cpp b/engines/mads/madsv2/core/player.cpp
index 04dc8e1714c..a8219d7b8aa 100644
--- a/engines/mads/madsv2/core/player.cpp
+++ b/engines/mads/madsv2/core/player.cpp
@@ -33,6 +33,7 @@
 #include "mads/madsv2/core/pal.h"
 #include "mads/madsv2/core/himem.h"
 #include "mads/madsv2/core/object.h"
+#include "mads/madsv2/engine.h"
 
 namespace MADS {
 namespace MADSV2 {
@@ -265,7 +266,7 @@ void player_select_series() {
 	player.sprite_changed = true;
 }
 
-static void player_activate_trigger() {
+void player_activate_trigger() {
 	int count;
 
 	player.commands_allowed |= player.enable_at_target;
@@ -283,12 +284,7 @@ static void player_activate_trigger() {
 	}
 }
 
-/**
- * When the player needs to turn to face a different direction
- * than his current facing, this routine rotates him one place
- * in the right direction.
- */
-static void player_keep_turning() {
+void player_keep_turning() {
 	int clockwise_count = 0;
 	int clockwise_sum = 0;
 	int counter_clockwise_count = 0;
@@ -478,105 +474,7 @@ void player_set_sprite() {
 }
 
 void player_keep_walking() {
-	int at_x, at_y;
-	int walk_code;
-	int id;
-	int new_facing = false;
-	int temp_velocity;
-	int angle_scale;
-	int angle_range;
-
-	while (player.walking && !player.walk_off_edge && (player.x == player.target_x) && (player.y == player.target_y)) {
-		if (rail_solution_stack_pointer == 0) {
-			if (player.walk_off_edge_to_room) {
-				player.walk_off_edge = player.walk_off_edge_to_room;
-				player.walk_anywhere = true;
-				player.walk_off_edge_to_room = 0;
-				player.commands_allowed = false;
-				new_facing = false;
-			} else {
-				player.walking = false;
-				player_set_final_facing();
-				new_facing = true;
-			}
-		} else {
-			id = rail_solution_stack[--rail_solution_stack_pointer];
-			player.target_x = room->rail[id].x;
-			player.target_y = room->rail[id].y;
-			new_facing = true;
-		}
-	}
-
-	if (new_facing) {
-		if (player.walking) player_set_facing();
-	}
-
-	if (player.facing != player.turn_to_facing) {
-		player_keep_turning();
-	} else {
-		if (!player.walking) {
-			player_new_stop_walker();
-			player_activate_trigger();
-		}
-	}
-
-	temp_velocity = player.velocity;
-
-	if (player.scaling_velocity && (player.total_distance > 0)) {
-		angle_range = 100 - player.scale;
-		angle_scale = player.scale + ((angle_range * (player.x_count - 1)) / player.total_distance);
-		temp_velocity = (int)(((long)temp_velocity * ((long)player.scale * (long)angle_scale)) / 10000L);
-		temp_velocity = MAX(temp_velocity, 1);
-	}
-
-	if (player.walking && (player.facing == player.turn_to_facing)) {
-		at_x = player.x;
-		at_y = player.y;
-		walk_code = false;
-		player.special_code = 0;
-
-		if (player.dist_accum < temp_velocity) {
-
-			do {
-				if (player.pixel_accum < player.x_count) {
-					player.pixel_accum += player.y_count;
-				}
-				if (player.pixel_accum >= player.x_count) {
-					if ((player.y_counter > 0) || player.walk_off_edge) at_y += player.sign_y;
-					player.y_counter--;
-					player.pixel_accum -= player.x_count;
-				}
-				if (player.pixel_accum < player.x_count) {
-					if ((player.x_counter > 0) || player.walk_off_edge) at_x += player.sign_x;
-					player.x_counter--;
-				}
-
-				if (!player.walk_anywhere && !(player.walk_off_edge || player.walk_off_edge_to_room)) {
-					walk_code |= attr_walk(&scr_walk, at_x, at_y);
-					if (!player.special_code) {
-						player.special_code = attr_special(&scr_special, at_x, at_y);
-					}
-				}
-
-				player.dist_accum += player.delta_distance;
-			} while ((player.dist_accum < temp_velocity) && (!walk_code) &&
-				((player.x_counter > 0) || (player.y_counter > 0) || (player.walk_off_edge)));
-
-		}
-
-		player.dist_accum -= temp_velocity;
-
-		if (walk_code) {
-			player_cancel_command();
-		} else {
-			if (!player.walk_off_edge) {
-				if (player.x_counter <= 0) at_x = player.target_x;
-				if (player.y_counter <= 0) at_y = player.target_y;
-			}
-			player.x = at_x;
-			player.y = at_y;
-		}
-	}
+	g_engine->player_keep_walking();
 }
 
 int player_search_image() {
diff --git a/engines/mads/madsv2/core/player.h b/engines/mads/madsv2/core/player.h
index 740a2126bb7..392ff7bb418 100644
--- a/engines/mads/madsv2/core/player.h
+++ b/engines/mads/madsv2/core/player.h
@@ -333,6 +333,15 @@ extern void player_demand_location(int x, int y);
 extern void player_first_walk(int from_x, int from_y, int from_facing,
 	int to_x, int to_y, int to_facing, int enable_at_target);
 
+/**
+ * When the player needs to turn to face a different direction
+ * than his current facing, this routine rotates him one place
+ * in the right direction.
+ */
+extern void player_keep_turning();
+
+extern void player_activate_trigger();
+
 } // namespace MADSV2
 } // namespace MADS
 
diff --git a/engines/mads/madsv2/dragonsphere/dragonsphere.cpp b/engines/mads/madsv2/dragonsphere/dragonsphere.cpp
index 2c0f343ff85..00f952aa103 100644
--- a/engines/mads/madsv2/dragonsphere/dragonsphere.cpp
+++ b/engines/mads/madsv2/dragonsphere/dragonsphere.cpp
@@ -21,14 +21,17 @@
 
 #include "engines/util.h"
 #include "mads/madsv2/console.h"
+#include "mads/madsv2/core/attr.h"
 #include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/env.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/matte.h"
 #include "mads/madsv2/core/object.h"
 #include "mads/madsv2/core/pal.h"
+#include "mads/madsv2/core/rail.h"
 #include "mads/madsv2/core/screen.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
@@ -1301,6 +1304,184 @@ void DragonsphereEngine::global_sound_driver() {
 	env_catint(kernel.sound_driver, new_section, 1);
 }
 
+void DragonsphereEngine::player_keep_walking() {
+	int at_x, at_y;
+	int walk_code;
+	int id;
+	int new_facing = false;
+	int temp_velocity;
+	int angle_scale;
+	int angle_range;
+
+	int countt;
+	int rum = 400;
+	int countt2 = global[oasis] - 1;
+
+	while (player.walking && !player.walk_off_edge && (player.x == player.target_x) && (player.y == player.target_y)) {
+		if (rail_solution_stack_pointer == 0) {
+			if (player.walk_off_edge_to_room) {
+
+				if (room_id == 302) {
+					if (player_said_2(walk_down, path_to_east) && game.difficulty == 0) {
+						if (!player_has_been_in_room(303)) {
+							global[bubble_wont_attack] = false;
+						}
+					}
+				}
+
+				if (room_id == 401 || room_id == 402 || room_id == 403) {
+					if (player_said_1(desert_to_east) || player_said_1(desert_to_west)) {
+						if (player_has_been_in_room(405) && player_said_1(desert_to_west)) {
+							global[from_direction] = FROM_EAST;
+							player.walk_off_edge_to_room = 405;
+
+						} else if (player_has_been_in_room(405) && player_said_1(desert_to_east)) {
+							global[pre_room] = 401;
+							if (player.walk_off_edge_to_room != 111) {
+								player.walk_off_edge_to_room = 120;
+							}
+
+						} else if (player_said_1(desert_to_east) && global[desert_room] % 7 == 0) {
+							global[pre_room] = 401;
+							if (player.walk_off_edge_to_room != 111) {
+								player.walk_off_edge_to_room = 120;
+							}
+
+						} else if (player_said_1(desert_to_east)) {
+							++global[desert_room];
+							global[from_direction] = FROM_WEST;
+
+						} else if (player_said_1(desert_to_west)) {
+							--global[desert_room];
+							global[from_direction] = FROM_EAST;
+						}
+
+						if (player.walk_off_edge_to_room != 120 &&
+							player.walk_off_edge_to_room != 111 &&
+							player.walk_off_edge_to_room != 405) for (countt = 0; countt < 77; countt++) {
+							++rum; if (rum == 404) rum = 401;
+							++countt2; if (countt2 == 78) countt2 = 1;
+
+							if (countt2 == global[desert_room]) {
+								if (global[desert_room] == 42)                 rum = 401;
+								if (global[desert_room] == global[oasis])      rum = 454;
+								if (global[desert_room] == global[fire_holes]) rum = 412;
+
+								if (player_said_1(desert_to_east) || player_said_1(desert_to_west)) {
+									if (room_id == rum) {
+										player.walk_off_edge_to_room = room_id;
+									} else {
+										player.walk_off_edge_to_room = rum;
+									}
+								}
+								goto over;
+							}
+						}
+					}
+				}
+
+over:
+				if ((room_id == 401 || room_id == 402 || room_id == 403) &&
+					!player_has_been_in_room(405)) {
+					++global[desert_counter];
+					if (global[desert_counter] == 6) {
+						player.walk_off_edge_to_room = 404;
+					}
+				}
+
+				player.walk_off_edge = player.walk_off_edge_to_room;
+				player.walk_anywhere = true;
+				player.walk_off_edge_to_room = 0;
+				player.commands_allowed = false;
+				new_facing = false;
+
+			} else {
+				player.walking = false;
+				player_set_final_facing();
+				new_facing = true;
+			}
+
+		} else {
+			id = rail_solution_stack[--rail_solution_stack_pointer];
+			player.target_x = room->rail[id].x;
+			player.target_y = room->rail[id].y;
+			new_facing = true;
+		}
+	}
+
+	if (new_facing) {
+		if (player.walking) player_set_facing();
+	}
+
+	if (player.facing != player.turn_to_facing) {
+		player_keep_turning();
+	} else {
+		if (!player.walking) {
+			player_new_stop_walker();
+			player_activate_trigger();
+		}
+	}
+
+	temp_velocity = player.velocity;
+
+	if (player.scaling_velocity && (player.total_distance > 0)) {
+		angle_range = 100 - player.scale;
+		angle_scale = player.scale + ((angle_range * (player.x_count - 1)) / player.total_distance);
+		temp_velocity = (int)(((long)temp_velocity * ((long)player.scale * (long)angle_scale)) / 10000L);
+		temp_velocity = MAX(temp_velocity, 1);
+	}
+
+	if (player.walking && (player.facing == player.turn_to_facing)) {
+		at_x = player.x;
+		at_y = player.y;
+		walk_code = false;
+		player.special_code = 0;
+
+		if (player.dist_accum < temp_velocity) {
+			do {
+				if (player.pixel_accum < player.x_count) {
+					player.pixel_accum += player.y_count;
+				}
+				if (player.pixel_accum >= player.x_count) {
+					if ((player.y_counter > 0) || player.walk_off_edge) at_y += player.sign_y;
+					player.y_counter--;
+					player.pixel_accum -= player.x_count;
+				}
+				if (player.pixel_accum < player.x_count) {
+					if ((player.x_counter > 0) || player.walk_off_edge) at_x += player.sign_x;
+					player.x_counter--;
+				}
+
+				if (!player.walk_anywhere && !(player.walk_off_edge || player.walk_off_edge_to_room)) {
+					walk_code |= attr_walk(&scr_walk, at_x, at_y);
+					if (!player.special_code) {
+						player.special_code = attr_special(&scr_special, at_x, at_y);
+					}
+				}
+
+				player.dist_accum += player.delta_distance;
+			} while ((player.dist_accum < temp_velocity) && (!walk_code) &&
+				((player.x_counter > 0) || (player.y_counter > 0) || (player.walk_off_edge)));
+		}
+
+		player.dist_accum -= temp_velocity;
+
+		if (walk_code) {
+			player_cancel_command();
+		} else {
+			if (!player.walk_off_edge) {
+				if (player.x_counter <= 0)
+					at_x = player.target_x;
+				if (player.y_counter <= 0)
+					at_y = player.target_y;
+			}
+
+			player.x = at_x;
+			player.y = at_y;
+		}
+	}
+}
+
 } // namespace Dragonsphere
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/dragonsphere/dragonsphere.h b/engines/mads/madsv2/dragonsphere/dragonsphere.h
index d5e3f721170..9d88ee47945 100644
--- a/engines/mads/madsv2/dragonsphere/dragonsphere.h
+++ b/engines/mads/madsv2/dragonsphere/dragonsphere.h
@@ -49,6 +49,7 @@ public:
 	void global_error_code() override;
 	void global_room_init() override;
 	void global_sound_driver() override;
+	void player_keep_walking() override;
 };
 
 } // namespace Dragonsphere
diff --git a/engines/mads/madsv2/engine.cpp b/engines/mads/madsv2/engine.cpp
index b81ee403683..e7fe891fcb9 100644
--- a/engines/mads/madsv2/engine.cpp
+++ b/engines/mads/madsv2/engine.cpp
@@ -376,5 +376,107 @@ SaveStateList MADSV2Engine::listSaves() const {
 	return getMetaEngine()->listSaves(_targetName.c_str());
 }
 
+void MADSV2Engine::player_keep_walking() {
+	int at_x, at_y;
+	int walk_code;
+	int id;
+	int new_facing = false;
+	int temp_velocity;
+	int angle_scale;
+	int angle_range;
+
+	while (player.walking && !player.walk_off_edge && (player.x == player.target_x) && (player.y == player.target_y)) {
+		if (rail_solution_stack_pointer == 0) {
+			if (player.walk_off_edge_to_room) {
+				player.walk_off_edge = player.walk_off_edge_to_room;
+				player.walk_anywhere = true;
+				player.walk_off_edge_to_room = 0;
+				player.commands_allowed = false;
+				new_facing = false;
+			} else {
+				player.walking = false;
+				player_set_final_facing();
+				new_facing = true;
+			}
+		} else {
+			id = rail_solution_stack[--rail_solution_stack_pointer];
+			player.target_x = room->rail[id].x;
+			player.target_y = room->rail[id].y;
+			new_facing = true;
+		}
+	}
+
+	if (new_facing) {
+		if (player.walking) player_set_facing();
+	}
+
+	if (player.facing != player.turn_to_facing) {
+		player_keep_turning();
+	} else {
+		if (!player.walking) {
+			player_new_stop_walker();
+			player_activate_trigger();
+		}
+	}
+
+	temp_velocity = player.velocity;
+
+	if (player.scaling_velocity && (player.total_distance > 0)) {
+		angle_range = 100 - player.scale;
+		angle_scale = player.scale + ((angle_range * (player.x_count - 1)) / player.total_distance);
+		temp_velocity = (int)(((long)temp_velocity * ((long)player.scale * (long)angle_scale)) / 10000L);
+		temp_velocity = MAX(temp_velocity, 1);
+	}
+
+	if (player.walking && (player.facing == player.turn_to_facing)) {
+		at_x = player.x;
+		at_y = player.y;
+		walk_code = false;
+		player.special_code = 0;
+
+		if (player.dist_accum < temp_velocity) {
+
+			do {
+				if (player.pixel_accum < player.x_count) {
+					player.pixel_accum += player.y_count;
+				}
+				if (player.pixel_accum >= player.x_count) {
+					if ((player.y_counter > 0) || player.walk_off_edge) at_y += player.sign_y;
+					player.y_counter--;
+					player.pixel_accum -= player.x_count;
+				}
+				if (player.pixel_accum < player.x_count) {
+					if ((player.x_counter > 0) || player.walk_off_edge) at_x += player.sign_x;
+					player.x_counter--;
+				}
+
+				if (!player.walk_anywhere && !(player.walk_off_edge || player.walk_off_edge_to_room)) {
+					walk_code |= attr_walk(&scr_walk, at_x, at_y);
+					if (!player.special_code) {
+						player.special_code = attr_special(&scr_special, at_x, at_y);
+					}
+				}
+
+				player.dist_accum += player.delta_distance;
+			} while ((player.dist_accum < temp_velocity) && (!walk_code) &&
+				((player.x_counter > 0) || (player.y_counter > 0) || (player.walk_off_edge)));
+
+		}
+
+		player.dist_accum -= temp_velocity;
+
+		if (walk_code) {
+			player_cancel_command();
+		} else {
+			if (!player.walk_off_edge) {
+				if (player.x_counter <= 0) at_x = player.target_x;
+				if (player.y_counter <= 0) at_y = player.target_y;
+			}
+			player.x = at_x;
+			player.y = at_y;
+		}
+	}
+}
+
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/engine.h b/engines/mads/madsv2/engine.h
index a7ff7a00890..6db62148dc5 100644
--- a/engines/mads/madsv2/engine.h
+++ b/engines/mads/madsv2/engine.h
@@ -112,6 +112,7 @@ public:
 	virtual void global_room_init() = 0;
 	virtual void global_sound_driver() = 0;
 	virtual void global_verb_filter() {}
+	virtual void player_keep_walking();
 
 	void playSpeech(Audio::AudioStream *stream);
 	void stopSpeech();


Commit: 7175f1472eda4246aab45ce435ac777a110f883d
    https://github.com/scummvm/scummvm/commit/7175f1472eda4246aab45ce435ac777a110f883d
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-28T21:12:19+10:00

Commit Message:
MADS: FOREST: Skeleton subengine codebase

Changed paths:
  A engines/mads/madsv2/forest/asound.cpp
  A engines/mads/madsv2/forest/asound.h
  A engines/mads/madsv2/forest/global.cpp
  A engines/mads/madsv2/forest/global.h
  A engines/mads/madsv2/forest/mads/inventory.h
  A engines/mads/madsv2/forest/mads/mads.cpp
  A engines/mads/madsv2/forest/mads/quotes.h
  A engines/mads/madsv2/forest/mads/sounds.h
  A engines/mads/madsv2/forest/mads/words.h
  A engines/mads/madsv2/forest/main.cpp
  A engines/mads/madsv2/forest/main.h
  A engines/mads/madsv2/forest/main_menu.cpp
  A engines/mads/madsv2/forest/main_menu.h
  A engines/mads/madsv2/forest/menus.cpp
  A engines/mads/madsv2/forest/menus.h
  A engines/mads/madsv2/forest/rooms/room101.cpp
  A engines/mads/madsv2/forest/rooms/room101.h
  A engines/mads/madsv2/forest/rooms/room103.cpp
  A engines/mads/madsv2/forest/rooms/room103.h
  A engines/mads/madsv2/forest/rooms/room104.cpp
  A engines/mads/madsv2/forest/rooms/room104.h
  A engines/mads/madsv2/forest/rooms/room106.cpp
  A engines/mads/madsv2/forest/rooms/room106.h
  A engines/mads/madsv2/forest/rooms/room107.cpp
  A engines/mads/madsv2/forest/rooms/room107.h
  A engines/mads/madsv2/forest/rooms/room199.cpp
  A engines/mads/madsv2/forest/rooms/room199.h
  A engines/mads/madsv2/forest/rooms/room201.cpp
  A engines/mads/madsv2/forest/rooms/room201.h
  A engines/mads/madsv2/forest/rooms/room203.cpp
  A engines/mads/madsv2/forest/rooms/room203.h
  A engines/mads/madsv2/forest/rooms/room204.cpp
  A engines/mads/madsv2/forest/rooms/room204.h
  A engines/mads/madsv2/forest/rooms/room205.cpp
  A engines/mads/madsv2/forest/rooms/room205.h
  A engines/mads/madsv2/forest/rooms/room210.cpp
  A engines/mads/madsv2/forest/rooms/room210.h
  A engines/mads/madsv2/forest/rooms/room211.cpp
  A engines/mads/madsv2/forest/rooms/room211.h
  A engines/mads/madsv2/forest/rooms/room220.cpp
  A engines/mads/madsv2/forest/rooms/room220.h
  A engines/mads/madsv2/forest/rooms/room221.cpp
  A engines/mads/madsv2/forest/rooms/room221.h
  A engines/mads/madsv2/forest/rooms/room301.cpp
  A engines/mads/madsv2/forest/rooms/room301.h
  A engines/mads/madsv2/forest/rooms/room302.cpp
  A engines/mads/madsv2/forest/rooms/room302.h
  A engines/mads/madsv2/forest/rooms/room303.cpp
  A engines/mads/madsv2/forest/rooms/room303.h
  A engines/mads/madsv2/forest/rooms/room304.cpp
  A engines/mads/madsv2/forest/rooms/room304.h
  A engines/mads/madsv2/forest/rooms/room305.cpp
  A engines/mads/madsv2/forest/rooms/room305.h
  A engines/mads/madsv2/forest/rooms/room306.cpp
  A engines/mads/madsv2/forest/rooms/room306.h
  A engines/mads/madsv2/forest/rooms/room307.cpp
  A engines/mads/madsv2/forest/rooms/room307.h
  A engines/mads/madsv2/forest/rooms/room308.cpp
  A engines/mads/madsv2/forest/rooms/room308.h
  A engines/mads/madsv2/forest/rooms/room321.cpp
  A engines/mads/madsv2/forest/rooms/room321.h
  A engines/mads/madsv2/forest/rooms/room322.cpp
  A engines/mads/madsv2/forest/rooms/room322.h
  A engines/mads/madsv2/forest/rooms/room401.cpp
  A engines/mads/madsv2/forest/rooms/room401.h
  A engines/mads/madsv2/forest/rooms/room402.cpp
  A engines/mads/madsv2/forest/rooms/room402.h
  A engines/mads/madsv2/forest/rooms/room403.cpp
  A engines/mads/madsv2/forest/rooms/room403.h
  A engines/mads/madsv2/forest/rooms/room404.cpp
  A engines/mads/madsv2/forest/rooms/room404.h
  A engines/mads/madsv2/forest/rooms/room405.cpp
  A engines/mads/madsv2/forest/rooms/room405.h
  A engines/mads/madsv2/forest/rooms/room420.cpp
  A engines/mads/madsv2/forest/rooms/room420.h
  A engines/mads/madsv2/forest/rooms/room501.cpp
  A engines/mads/madsv2/forest/rooms/room501.h
  A engines/mads/madsv2/forest/rooms/room503.cpp
  A engines/mads/madsv2/forest/rooms/room503.h
  A engines/mads/madsv2/forest/rooms/room509.cpp
  A engines/mads/madsv2/forest/rooms/room509.h
  A engines/mads/madsv2/forest/rooms/room510.cpp
  A engines/mads/madsv2/forest/rooms/room510.h
  A engines/mads/madsv2/forest/rooms/room520.cpp
  A engines/mads/madsv2/forest/rooms/room520.h
  A engines/mads/madsv2/forest/rooms/room901.cpp
  A engines/mads/madsv2/forest/rooms/room901.h
  A engines/mads/madsv2/forest/rooms/room903.cpp
  A engines/mads/madsv2/forest/rooms/room903.h
  A engines/mads/madsv2/forest/rooms/room904.cpp
  A engines/mads/madsv2/forest/rooms/room904.h
  A engines/mads/madsv2/forest/rooms/section1.cpp
  A engines/mads/madsv2/forest/rooms/section1.h
  A engines/mads/madsv2/forest/rooms/section2.cpp
  A engines/mads/madsv2/forest/rooms/section2.h
  A engines/mads/madsv2/forest/rooms/section3.cpp
  A engines/mads/madsv2/forest/rooms/section3.h
  A engines/mads/madsv2/forest/rooms/section4.cpp
  A engines/mads/madsv2/forest/rooms/section4.h
  A engines/mads/madsv2/forest/rooms/section5.cpp
  A engines/mads/madsv2/forest/rooms/section5.h
  A engines/mads/madsv2/forest/rooms/section9.cpp
  A engines/mads/madsv2/forest/rooms/section9.h
  A engines/mads/madsv2/forest/sound_forest.cpp
  A engines/mads/madsv2/forest/sound_forest.h
    engines/mads/madsv2/forest/forest.cpp
    engines/mads/madsv2/forest/forest.h
    engines/mads/module.mk


diff --git a/engines/mads/madsv2/forest/asound.cpp b/engines/mads/madsv2/forest/asound.cpp
new file mode 100644
index 00000000000..70022f4fdb8
--- /dev/null
+++ b/engines/mads/madsv2/forest/asound.cpp
@@ -0,0 +1,1805 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "audio/fmopl.h"
+#include "mads/madsv2/forest/asound.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+
+bool AdlibChannel::_isDisabled;
+
+/*
+  * PATCH_ATTEN_TO_TL  (seg001:0x0092 / offset from _asound_samples base)
+  * patchAttenuation (0-127) -> 6-bit OPL total-level.
+  * The Dragonsphere asm uses PATCH_ATTEN_TO_TL[bx] for the modulator lookup
+  * and unk_12431 - bx (i.e. PATCH_ATTEN_TO_TL[127 - patchAtt]) for the
+  * carrier lookup.
+  */
+static const uint8 PATCH_ATTEN_TO_TL[128] = {
+	63, 54, 49, 45, 42, 40, 38, 36, 34, 33, 32, 31, 30, 29, 28, 27,
+	26, 25, 25, 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18,
+	18, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 14, 14, 13, 13, 13,
+	12, 12, 12, 12, 11, 11, 11, 11, 11, 10, 10, 10, 10,  9,  9,  9,
+	 9,  9,  8,  8,  8,  8,  8,  7,  7,  7,  7,  7,  7,  6,  6,  6,
+	 6,  6,  6,  5,  5,  5,  5,  5,  5,  5,  4,  4,  4,  4,  4,  4,
+	 4,  3,  3,  3,  3,  3,  3,  3,  3,  2,  2,  2,  2,  2,  2,  2,
+	 2,  2,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0
+};
+
+/*
+ * VOL_VEL_TO_ATTEN_STEP  (_volVelToAttenStep)
+ * volume or velocity byte (0-127) -> attenuation step (1-32).
+ * Groups of 4 inputs map to the same step.
+ */
+static const uint8 VOL_VEL_TO_ATTEN_STEP[128] = {
+	 1, 1, 1, 1,  2, 2, 2, 2,  3, 3, 3, 3,  4, 4, 4, 4,
+	 5, 5, 5, 5,  6, 6, 6, 6,  7, 7, 7, 7,  8, 8, 8, 8,
+	 9, 9, 9, 9, 10,10,10,10, 11,11,11,11, 12,12,12,12,
+	13,13,13,13, 14,14,14,14, 15,15,15,15, 16,16,16,16,
+	17,17,17,17, 18,18,18,18, 19,19,19,19, 20,20,20,20,
+	21,21,21,21, 22,22,22,22, 23,23,23,23, 24,24,24,24,
+	25,25,25,25, 26,26,26,26, 27,27,27,27, 28,28,28,28,
+	29,29,29,29, 30,30,30,30, 31,31,31,31, 32,32,32,32
+};
+
+/*
+ * SEMITONE_FREQ_TABLE  (_semitoneFreqTable)
+ * OPL F-number base for each semitone within an octave.
+ */
+static const uint16 SEMITONE_FREQ_TABLE[12] = {
+	0x0200, 0x021E, 0x023F, 0x0261, 0x0285, 0x02AB,
+	0x02D4, 0x02FF, 0x032D, 0x035D, 0x0390, 0x03C7
+};
+
+/*
+ * VOICE_SLOTS  (byte_1239B in the binary, also used as the operator-reg
+ * index table for command6/7)
+ *
+ * Layout for each voice: { slot0 (modulator), slot1 (carrier) }
+ * The writeVolume loop uses:
+ *   pass 0 -> VOICE_SLOTS[ch][0]  (modulator)
+ *   pass 1 -> VOICE_SLOTS[ch][1]  (carrier)
+ * The alg!=0 single-op path (loc_11692) goes directly to VOICE_SLOTS[ch][1].
+ */
+static const uint8 VOICE_SLOTS[ADLIB_CHANNEL_COUNT][2] = {
+	{  0,  3 }, {  1,  4 }, {  2,  5 },
+	{  6,  9 }, {  7, 10 }, {  8, 11 },
+	{ 12, 15 }, { 13, 16 }, { 14, 17 }
+};
+
+/*
+ * SLOT_TO_REG_OFFSET  (_slotToRegOffset)
+ * operator-slot index (0-17) -> OPL register group offset.
+ */
+static const uint8 SLOT_TO_REG_OFFSET[18] = {
+	 0,  1,  2,  3,  4,  5,
+	 8,  9, 10, 11, 12, 13,
+	16, 17, 18, 19, 20, 21
+};
+
+/*
+ * byte_1239B  - all 22 operator TL register indices muted/restored by
+ * command6 and command7.  They cover every OPL operator slot (0x40-0x55).
+ */
+static const uint8 ALL_OP_TL_REGS[22] = {
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55
+};
+
+/* Null / silence sound-data placeholder stream. */
+static uint8 ADLIB_NULLDATA[] = {
+	0x00, 0x01, 0xF3, 0x00, 0x00, 0xEF, 0x00, 0x00,
+	0xF5, 0x00, 0x00, 0x00, 0xF7, 0x00, 0xF8, 0x1D,
+	0xFF, 0x00, 0xF4, 0x6E, 0x2A, 0x1C, 0xF4, 0x5F,
+	0x2A, 0x1C, 0x2A, 0x1C, 0x2A, 0x1C, 0xFF, 0xFF,
+	0x00, 0x00, 0x00, 0x00
+};
+
+/* OPL version flag (_adlib_v5660 / opl_version_flag).
+ * 0x18 = OPL3 (patch-attenuation-aware writeVolume path). */
+constexpr uint16 OPL_VERSION_FLAG = 0x18;
+
+/* Rhythm-mode flags (from data segment). */
+constexpr bool RHYTHM_HI_HAT = true;
+constexpr bool RHYTHM_CYMBAL = true;
+constexpr bool RHYTHM_ENABLE = true;
+
+AdlibSample::AdlibSample(Common::SeekableReadStream &s) {
+	_attackRate = s.readByte();
+	_decayRate = s.readByte();
+	_sustainLevel = s.readByte();
+	_releaseRate = s.readByte();
+	_egTyp = s.readByte();
+	_ksr = s.readByte();
+	_totalLevel = s.readByte();
+	_scalingLevel = s.readByte();
+	_waveformSelect = s.readByte();
+	_freqMultiple = s.readByte();
+	_feedback = s.readByte();
+	_ampMod = s.readByte();
+	_vib = s.readByte();
+	_alg = s.readByte();
+	_freqSweepInit = s.readByte();
+	_reserved = s.readByte();
+	_freqMask = s.readUint16LE();
+	_freqBase = s.readUint16LE();
+	_outerLoopPtr = s.readUint16LE();
+}
+
+void AdlibChannel::reset() {
+	/* Zero the scalar "live" fields only.
+	 *
+	 * From AdlibChannel_reset in the binary: clears _activeCount, _pitchBend,
+	 * _volumeFadeStep, _vibratoDepth, _arpCounterReload (field_13),
+	 * _writeVolumePending (field_11), _transpose, _patchAttenuation,
+	 * _pendingStop, _velocity, _volume, _freqSweepCounter, _noiseFreqMask,
+	 * _freqAccum, _freqStep, _loopStartPtr, _pSrc.
+	 * All other fields are left as-is (they are overwritten by load() or
+	 * by the bytecode stream before being read).
+	 */
+	_activeCount = 0;
+	_pitchBend = 0;
+	_volumeFadeStep = 0;
+	_vibratoDepth = 0;
+	_arpCounterReload = 0;   /* field_13 */
+	_writeVolumePending = 0;   /* field_11 */
+	_transpose = 0;
+	_patchAttenuation = 0;
+	_pendingStop = 0;
+	_velocity = 0;
+	_volume = 0;
+	_freqSweepCounter = 0;
+	_noiseFreqMask = 0;
+	_freqAccum = 0;
+	_freqStep = 0;
+	_loopStartPtr = nullptr;
+	_pSrc = nullptr;
+}
+
+void AdlibChannel::load(byte *soundData) {
+	_isDisabled = true;
+
+	/* Zero all bytes in the struct. */
+	memset(this, 0, sizeof(AdlibChannel));
+
+	/* Initialise all loop/source pointers to the supplied data block. */
+	_loopStartPtr = soundData;
+	_pSrc = soundData;
+	_innerLoopPtr = soundData;
+	_outerLoopPtr = soundData;
+	_soundData = soundData;
+
+	/* Start at maximum attenuation (silent) and mark the channel active. */
+	_patchAttenuation = 0x40;
+	_activeCount = 1;
+
+	_isDisabled = false;
+}
+
+void AdlibChannel::setPtr2(byte *ptr) {
+	/* Hard-redirect both read pointers, then arm a one-step fade. */
+	_loopStartPtr = ptr;
+	_pSrc = ptr;
+	_volumeFadeStep = 0xFF;   /* -1 signed: fade down */
+	_fadePeriodReload = 1;
+	_fadePeriodCounter = 1;
+}
+
+void AdlibChannel::enable() {
+	/* AdlibChannel_enable: mark pending-stop on active channels only. */
+	if (_activeCount == 0)
+		return;
+	_pendingStop = 0xFF;
+	_soundData = ADLIB_NULLDATA;
+}
+
+void AdlibChannel::processChannelFade() {
+	if (_activeCount == 0)
+		return;
+	if (_pendingStop == 0)
+		return;
+
+	/* Channel has fully faded when both velocity and volume are zero. */
+	if (_velocity == 0 && _volume == 0) {
+		_loopStartPtr = ADLIB_NULLDATA;
+		_pSrc = ADLIB_NULLDATA;
+		_pendingStop = 0;
+		return;
+	}
+
+	/* Arm a quick fade-down (period 2, step -1). */
+	_volumeFadeStep = 0xFF;
+	_fadePeriodReload = 2;
+	if (_fadePeriodCounter == 0)
+		_fadePeriodCounter = 1;
+}
+
+ASound::ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
+	int dataOffset, int dataSize)
+	: SoundDriver(mixer, opl, filename, dataOffset, dataSize) {
+	AdlibChannel::_isDisabled = false;
+
+	/* Standard OPL timer-reset sequence. */
+	write(4, 0x60);
+	write(4, 0x80);
+	write(2, 0xFF);
+	write(4, 0x21);
+	write(4, 0x60);
+	write(4, 0x80);
+
+	Common::fill(_adlibPorts, _adlibPorts + 256, 0);
+	command0();
+
+	_opl->start(new Common::Functor0Mem<void, ASound>(this, &ASound::onTimer),
+		CALLBACKS_PER_SECOND);
+}
+
+int ASound::stop() {
+	command0();
+	int result = _pollResult;
+	_pollResult = 0;
+	return result;
+}
+
+int ASound::poll() {
+	update();
+	int result = _pollResult;
+	_pollResult = 0;
+	return result;
+}
+
+void ASound::noise() {
+	Common::StackLock slock(_driverMutex);
+	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+		noise_inner(i);
+}
+
+int ASound::command0() {
+	/* Push _isDisabled, temporarily force to 0xFFFF while resetting. */
+	uint16 savedDisabled = _isDisabled;
+	_isDisabled = 0xFFFF;
+
+	/* 1. Reset all 9 channels. */
+	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+		_channels[i]->reset();
+
+	/* 2. Mute all operator TL registers (0x40 down to 0x4F, via adlib_write2
+	 *    with value 0x3F, stepping from 0x4F down while reg >= 0x40). */
+	for (int reg = 0x4F; reg >= 0x40; --reg)
+		write((uint8)reg, 0x3F);
+
+	/* 3. Zero registers 0xFF down to 0x60. */
+	for (int reg = 0xFF; reg >= 0x60; --reg)
+		write((uint8)reg, 0x00);
+
+	/* 4. Zero registers 0x3F down to 0x01. */
+	for (int reg = 0x3F; reg >= 0x01; --reg)
+		write((uint8)reg, 0x00);
+
+	/* 5. Waveform Select Enable. */
+	write(0x01, 0x20);
+
+	/* 6. Reset tick callback. */
+	resetCallback();
+
+	_isDisabled = savedDisabled;
+	return 0;
+}
+
+int ASound::command1() {
+	/* Fade all channels: music 0-6 (command3) then SFX 7-8 (command5). */
+	command3();
+	command5();
+	return 0;
+}
+
+int ASound::command2() {
+	/* Hard-redirect music channels 0-6 to null stream with setPtr2. */
+	_channel0.setPtr2(ADLIB_NULLDATA);
+	_channel1.setPtr2(ADLIB_NULLDATA);
+	_channel2.setPtr2(ADLIB_NULLDATA);
+	_channel3.setPtr2(ADLIB_NULLDATA);
+	_channel4.setPtr2(ADLIB_NULLDATA);
+	_channel5.setPtr2(ADLIB_NULLDATA);
+	_channel6.setPtr2(ADLIB_NULLDATA);
+	return 0;
+}
+
+int ASound::command3() {
+	/* Pending-stop music channels 0-6 (natural envelope fade via enable). */
+	_channel0.enable();
+	_channel1.enable();
+	_channel2.enable();
+	_channel3.enable();
+	_channel4.enable();
+	_channel5.enable();
+	_channel6.enable();
+	return 0;
+}
+
+int ASound::command4() {
+	/* Hard-redirect SFX channels 7-8. */
+	_channel7.setPtr2(ADLIB_NULLDATA);
+	_channel8.setPtr2(ADLIB_NULLDATA);
+	return 0;
+}
+
+int ASound::command5() {
+	/* Pending-stop SFX channels 7-8. */
+	_channel7.enable();
+	_channel8.enable();
+	return 0;
+}
+
+int ASound::command6() {
+	/* Guard: already disabled. */
+	if (_isDisabled == 0xFFFF)
+		return 0;
+	_isDisabled = 0xFFFF;
+
+	/* Save each channel's _freqSweepCounter -> _savedSweepCounter, then zero it. */
+	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) {
+		AdlibChannel *ch = _channels[i];
+		ch->_savedSweepCounter = ch->_freqSweepCounter;
+		ch->_freqSweepCounter = 0;
+	}
+
+	/* Mute all 22 operator TL registers. */
+	for (int i = 0; i < 22; ++i)
+		adlib_channelOff(ALL_OP_TL_REGS[i]);
+
+	return 0;
+}
+
+int ASound::command7() {
+	/* Guard: only resume from a paused state. */
+	if (_isDisabled != 0xFFFF)
+		return 0;
+
+	/* Restore all operator volumes from the _adlibPorts shadow
+	 * (asound_playMusicC: just re-writes whatever was last written). */
+	for (int i = 0; i < 22; ++i)
+		adlib_channelOn(ALL_OP_TL_REGS[i]);
+
+	/* Restore each channel's sweep counter and check whether any was non-zero. */
+	uint8 anySweep = 0;
+	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i) {
+		AdlibChannel *ch = _channels[i];
+		ch->_freqSweepCounter = ch->_savedSweepCounter;
+		anySweep |= ch->_savedSweepCounter;
+	}
+
+	if (anySweep != 0)
+		signalSoundPlaying();
+
+	_isDisabled = 0;
+	return 0;
+}
+
+int ASound::command8() {
+	/* Returns non-zero if any channel is currently active.
+	 * Clears byte_12393 (music-only flag) first so all 9 channels are checked. */
+	_musicOnlyFlag = 0;
+	uint8 result = 0;
+	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+		result |= _channels[i]->_activeCount;
+	return result;
+}
+
+int ASound::command18() {
+	/* Re-entrant background-music launcher (asound_command18 in the binary).
+	 * Fades everything, then dispatches back through the command table using
+	 * _musicIndex (word_12370) as the command ID. */
+	command1();
+	return command(_musicIndex, 0);
+}
+
+void ASound::callFunction(uint16 offset) {
+	error("Unsupported call to sound driver function at offset %.4x", offset);
+}
+
+void ASound::write(uint8 reg, uint8 value) {
+	_adlibPorts[reg] = value;
+	_opl->writeReg(reg, value);
+}
+
+void ASound::onTimer() {
+	Common::StackLock slock(_driverMutex);
+	poll();
+}
+
+uint16 ASound::getRandomNumber() {
+	/* asound_getRandomNumber: ax = ror3(0x9248 + seed). */
+	uint16 ax = (uint16)(0x9248u + _randomSeed);
+	ax = (uint16)((ax >> 3) | (ax << 13));
+	_randomSeed = ax;
+	return ax;
+}
+
+void ASound::adlib_channelOff(uint8 portIndex) {
+	/* sub_1018F: OR the register with 0x3F (force max attenuation),
+	 * then write back both to _adlibPorts and to the OPL chip.
+	 * Note: unlike the Phantom driver, the original value is NOT preserved
+	 * in _adlibPorts - the ORed value is stored back. */
+	uint8 val = _adlibPorts[portIndex] | 0x3F;
+	_adlibPorts[portIndex] = val;
+	_opl->writeReg(portIndex, val);
+}
+
+void ASound::adlib_channelOn(uint8 portIndex) {
+	/* asound_playMusicC: re-writes whatever _adlibPorts already holds.
+	 * command7 simply replays the shadow without recalculating anything. */
+	_opl->writeReg(portIndex, _adlibPorts[portIndex]);
+}
+
+void ASound::signalSoundPlaying() {
+	/* asound_signalSoundPlaying: set resultFlag/pollResult to non-zero. */
+	if (_resultFlag == (int16)0xFFFF)
+		return;
+	_resultFlag = (int16)0xFFFF;
+	_pollResult = (int16)0xFFFF;
+}
+
+void ASound::clearCallback() {
+	_callbackFnPtr = nullptr;
+	_callbackCounter = 0;
+	_callbackPeriod = 0;
+}
+
+void ASound::tickCallback() {
+	if (_callbackPeriod == 0)
+		return;
+	if (--_callbackCounter != 0)
+		return;
+
+	_callbackCounter = _callbackPeriod;
+	if (_callbackFnPtr == nullptr)
+		return;
+
+	auto fn = _callbackFnPtr;
+	_callbackFnPtr = nullptr;
+	(this->*fn)();
+}
+
+void ASound::writeVolume() {
+	AdlibChannel *ch = _activeChannelPtr;
+	uint8 chanNum = _activeChannelNumber;
+
+	/* Compute combined attenuation step from volume and velocity. */
+	uint8  volIdx = ch->_volume;
+	uint8  velIdx = ch->_velocity;
+	int16  volStep = (int16)(uint16)VOL_VEL_TO_ATTEN_STEP[volIdx];
+	int16  velStep = (int16)(uint16)VOL_VEL_TO_ATTEN_STEP[velIdx];
+	int16  var4 = volStep + velStep - 1;   /* var_4: combined step (shared) */
+
+	/* Check _alg of the first sample to determine loop count. */
+	AdlibSample *smpFirst = &_samples[ch->_sampleIndex * 2];
+	int passes = (smpFirst->_alg == 0) ? 2 : 1;
+
+	/* si and di track the two TL values written per pass for caching. */
+	int16 finalSi = 0, finalDi = 0;
+
+	/* var_6 = loop counter (0, then 1 for the two-pass case).
+	 * For alg != 0 we jump straight into the carrier path (var_6 stays at
+	 * effectively 1: VOICE_SLOTS[ch][1]). */
+	for (int var6 = (passes == 1 ? 1 : 0); var6 < 2; ++var6) {
+
+		/* Reload var_2 = var_4 at the start of each pass (loc_11642). */
+		int16 var2 = var4;
+
+		/* Select the operator slot. */
+		uint8 slot = VOICE_SLOTS[chanNum][var6];
+		uint8 regOff = SLOT_TO_REG_OFFSET[slot];
+		uint16 tlReg = (uint16)regOff + 0x40;   /* var_8 */
+
+		/* KSL bits from the port shadow. */
+		int16 kslBits = (int16)((uint16)_adlibPorts[tlReg] & 0xC0);   /* var_A */
+
+		int16 si, di;
+
+		if (OPL_VERSION_FLAG < 0x18) {
+			/* ---- OPL2 simple path (loc_1167C / loc_1167C equivalent) ---- */
+			int16 tl = (int16)0x3F - var2;
+			tl |= kslBits;
+			si = tl;
+			di = tl;
+			/* adlib_write2(8, tlReg, tl) */
+			write((uint8)tlReg, (uint8)tl);
+		} else {
+			/* ---- OPL3 patch-attenuation path (loc_115BE / loc_116D4) ---- */
+			uint8 pa = ch->_patchAttenuation;
+
+			/* Modulator TL (first register, offset 0): */
+			int16 tlMod = (int16)(uint16)PATCH_ATTEN_TO_TL[pa];
+			/* -(tlMod - var2) = var2 - tlMod */
+			int16 atten1 = var2 - tlMod;   /* ax = si initially */
+			si = atten1;
+			if (si < 0)       si = 0;
+			else if (si > 63) si = 63;
+			int16 reg0val = ((int16)0x3F - si) | kslBits;
+			si = reg0val;
+			write((uint8)tlReg, (uint8)reg0val);
+
+			/* Carrier TL (second register, offset 2):
+			 * unk_12431 - bx (where bx = pa) == PATCH_ATTEN_TO_TL[127 - pa]. */
+			int16 tlCar = (int16)(uint16)PATCH_ATTEN_TO_TL[127 - pa];
+			/* di = var_2 - tlCar  (var_4 for alg!=0, var_2=var_4 reload for alg==0) */
+			di = var2 - tlCar;
+			int16 diClamped = di;
+			if (diClamped < 0)        diClamped = 0;
+			else if (diClamped > 0x3F) diClamped = 0x3F;
+			int16 reg2val = ((int16)0x3F - diClamped) | kslBits;
+			di = reg2val;
+			write((uint8)(tlReg + 2), (uint8)reg2val);
+		}
+
+		/* For the alg!=0 single-pass case we only run this once (var6==1).
+		 * For the alg==0 two-pass case, record both sets. */
+		if (var6 == 1) {
+			finalSi = si;
+			finalDi = di;
+		} else {
+			/* var6==0: record si/di for the mod pass; di is overwritten on
+			 * the carrier pass so we use si for the high byte of field_2A. */
+			finalSi = si;
+			finalDi = di;
+		}
+	}
+
+	/* Cache the written TL bytes in field_2A:
+	 *   dl = di & 0x3F  (carrier / second register)
+	 *   dh = si & 0x3F  (modulator / first register)
+	 * Packed as a word at offset 0x2A. */
+	ch->_cachedCarrierTL = (uint8)(finalDi & 0x3F);   /* dl -> field_2A low byte  */
+	/* The high byte (si & 0x3F) is stored in _savedFreqSweep (offset 0x2B)
+	 * by the assembly.  We write it separately to avoid clobbering the real
+	 * savedFreqSweep which lives there - the original asm stores both bytes
+	 * as a word at [bx+2Ah], meaning field_2A=dl and field_2B=dh=si&0x3F.
+	 * In our C++ layout _savedFreqSweep is at 0x2B; we shadow the dh byte
+	 * into it only when writing volume, consistent with the original. */
+	ch->_savedFreqSweep = (uint8)(finalSi & 0x3F);
+}
+
+void ASound::writeFrequency() {
+	AdlibChannel *ch = _activeChannelPtr;
+	uint8 chanNum = _activeChannelNumber;
+	uint16 aReg = (uint16)chanNum + 0xA0;
+	uint16 bReg = (uint16)chanNum + 0xB0;
+
+	/* Note is 1-based; _octaveTranspose shifts by whole octaves. */
+	int note = (int)ch->_note + (int)ch->_octaveTranspose - 1;
+	int octave = note / 12;
+	int semi = note % 12;
+	if (semi < 0) {
+		semi += 12; --octave;
+	}
+
+	/* F-number from table, with optional signed transpose offset. */
+	int16 fnum = (int16)SEMITONE_FREQ_TABLE[semi] + (int16)(int8)ch->_transpose;
+
+	write((uint8)aReg, (uint8)fnum);
+
+	uint8 bVal = _adlibPorts[bReg] & 0x20;          /* preserve key-on bit */
+	bVal |= (uint8)((fnum >> 8) & 0x03);            /* F-num bits 9:8 */
+	bVal |= (uint8)(((uint8)octave & 0x07) << 2);   /* block/octave bits 4:2 */
+	write((uint8)bReg, bVal);
+}
+
+void ASound::writePitchBend() {
+	AdlibChannel *ch = _activeChannelPtr;
+	uint8 chanNum = _activeChannelNumber;
+	uint16 aReg = (uint16)chanNum + 0xA0;
+	uint16 bReg = (uint16)chanNum + 0xB0;
+
+	/* Reconstruct the 10-bit F-number from the port shadow. */
+	uint16 fnum = (uint16)_adlibPorts[aReg]
+		| ((uint16)(_adlibPorts[bReg] & 0x1F) << 8);
+
+	int16 bent = (int16)fnum + (int16)(int8)ch->_pitchBend;
+
+	write((uint8)aReg, (uint8)bent);
+
+	/* Preserve block + key-on, update F-num[9:8]. */
+	uint8 bVal = _adlibPorts[bReg] & 0x20;
+	bVal |= (uint8)((bent >> 8) & 0x03);
+	write((uint8)bReg, bVal);
+}
+
+void ASound::writeArpeggio() {
+	AdlibChannel *ch = _activeChannelPtr;
+	uint8 chanNum = _activeChannelNumber;
+	uint16 aReg = (uint16)chanNum + 0xA0;
+	uint16 bReg = (uint16)chanNum + 0xB0;
+
+	/* dl = _note + _octaveTranspose + _writeVolumePending - 1 */
+	int note = (int)ch->_note + (int)ch->_octaveTranspose
+		+ (int)ch->_writeVolumePending - 1;
+	int octave = note / 12;
+	int semi = note % 12;
+	if (semi < 0) {
+		semi += 12; --octave;
+	}
+
+	uint16 freqEntry = SEMITONE_FREQ_TABLE[semi];
+	uint8  fnHigh = (uint8)((freqEntry >> 8) & 0x03);
+	uint8  block = (uint8)(octave & 0x07);
+	uint8  bVal = (uint8)((block << 2) | fnHigh) | (_adlibPorts[bReg] & 0x20);
+
+	write((uint8)aReg, (uint8)freqEntry);
+	write((uint8)bReg, bVal);
+}
+
+void ASound::updateOctave() {
+	uint8 chanNum = _activeChannelNumber;
+	uint16 bReg = (uint16)chanNum + 0xB0;
+	write((uint8)bReg, _adlibPorts[bReg] & 0xDF);
+}
+
+void ASound::noteOn() {
+	AdlibChannel *ch = _activeChannelPtr;
+	AdlibSample *smp = &_samples[ch->_sampleIndex];
+
+	writeVolume();
+
+	ch->_freqSweepCounter = smp->_freqSweepInit;
+
+	if (ch->_freqSweepCounter != 0) {
+		/* Sweep is pending; signal that sound is active but don't key on yet. */
+		signalSoundPlaying();
+		return;
+	}
+
+	/* No sweep - write frequency and set key-on immediately. */
+	writeFrequency();
+
+	uint8 chanNum = _activeChannelNumber;
+	uint16 bReg = (uint16)chanNum + 0xB0;
+	write((uint8)bReg, _adlibPorts[bReg] | 0x20);
+}
+
+void ASound::writeSampleRegs() {
+	AdlibSample *smp = _samplePtr;
+	uint16 base = _currentOpBase;
+
+	/* 0xBD: rhythm mode flags. */
+	uint8 bdVal = _adlibPorts[0xBD] & 0x3F;
+	if (RHYTHM_HI_HAT) bdVal |= 0x80;
+	if (RHYTHM_CYMBAL) bdVal |= 0x40;
+	write(0xBD, bdVal);
+
+	/* 0x08: note-select / rhythm enable. */
+	write(0x08, RHYTHM_ENABLE ? 0x40 : 0x00);
+
+	/* 0xC0+voice: feedback and algorithm.
+	 * asm: feedback << 1, then alg==1 -> bit0=0, else -> bit0=1. */
+	write((uint8)(_activeChannelReg + 0xC0),
+		(uint8)((smp->_feedback << 1) | (smp->_alg == 1 ? 0 : 1)));
+
+	/* 0x60+base: attack / decay. */
+	write((uint8)(base + 0x60),
+		(uint8)(((smp->_attackRate & 0x0F) << 4) | (smp->_decayRate & 0x0F)));
+
+	/* 0x80+base: sustain level / release rate. */
+	write((uint8)(base + 0x80),
+		(uint8)(((smp->_sustainLevel & 0x0F) << 4) | (smp->_releaseRate & 0x0F)));
+
+	/* 0x20+base: AM/VIB/EGT/KSR/mult flags. */
+	uint8 amVal = smp->_freqMultiple & 0x0F;
+	if (smp->_egTyp == 1) amVal |= 0x20;
+	if (smp->_vib == 1) amVal |= 0x40;
+	if (smp->_ksr == 1) amVal |= 0x10;
+	if (smp->_ampMod == 1) amVal |= 0x80;
+	write((uint8)(base + 0x20), amVal);
+
+	/* 0xE0+base: waveform select. */
+	write((uint8)(base + 0xE0), (uint8)(smp->_waveformSelect & 0x03));
+}
+
+void ASound::loadSample() {
+	AdlibChannel *ch = _activeChannelPtr;
+	uint8 chanNum = _activeChannelNumber;
+
+	/* Phase 1: Silence modulator operator (slot 0). */
+	uint8 mSlot = VOICE_SLOTS[chanNum][0];
+	uint8 mRegOff = SLOT_TO_REG_OFFSET[mSlot];
+	write((uint8)(mRegOff + 0x80), 0xFF);
+	write((uint8)(mRegOff + 0x40), 63);
+
+	/* Phase 2: Silence carrier operator (slot 1). */
+	uint8 cSlot = VOICE_SLOTS[chanNum][1];
+	uint8 cRegOff = SLOT_TO_REG_OFFSET[cSlot];
+	write((uint8)(cRegOff + 0x80), 0xFF);
+	write((uint8)(cRegOff + 0x40), 63);
+
+	/* Phase 3: Write first sample (modulator) registers. */
+	_activeChannelReg = (uint16)chanNum;
+	_currentOpBase = mRegOff;
+	_samplePtr = &_samples[ch->_sampleIndex * 2];
+	writeSampleRegs();
+
+	/* Phase 4: Write second sample (carrier) registers. */
+	_currentOpBase = cRegOff;
+	_samplePtr = &_samples[ch->_sampleIndex * 2 + 1];
+	writeSampleRegs();
+
+	/* Phase 5: Write carrier TL (KSL | 0x3F for full attenuation initially). */
+	write((uint8)(cRegOff + 0x40),
+		(uint8)((_samplePtr->_scalingLevel << 6) | 0x3F));
+
+	/* Phase 6: Use the first sample again for the modulator TL and for
+	 * sweep/frequency initialisation. */
+	AdlibSample *smp3 = &_samples[ch->_sampleIndex * 2];
+	uint16        mTLReg = (uint16)mRegOff + 0x40;
+
+	if (smp3->_alg == 0) {
+		write((uint8)mTLReg, (uint8)((smp3->_scalingLevel << 6) | 0x3F));
+	} else {
+		/* alg != 0: negate (totalLevel - 63) to get the TL value. */
+		uint8 tl = (uint8)(-(int8)(smp3->_totalLevel - 63));
+		write((uint8)mTLReg, (uint8)((smp3->_scalingLevel << 6) | (tl & 0x3F)));
+	}
+
+	/* Copy sweep/frequency parameters from the sample into the channel. */
+	ch->_freqSweepCounter = smp3->_freqSweepInit;
+	ch->_noiseFreqMask = smp3->_freqMask;
+	ch->_freqAccum = smp3->_freqBase;
+	ch->_freqStep = smp3->_outerLoopPtr;
+}
+
+void ASound::update() {
+	if (_isDisabled)
+		return;
+
+	(void)getRandomNumber();
+	++_frameNumber2;
+
+	pollAllChannels();
+	tickCallback();       /* asound_updateCallback */
+	updateAllChannels();
+
+	_anySweepActive = 0;
+
+	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+		update1(i);
+
+	/* If no sweep is active, flag the result as "done". */
+	if (!_anySweepActive) {
+		if (_resultFlag != (int16)0xFFFF) {
+			_resultFlag = (int16)0xFFFF;
+			_pollResult = (int16)0xFFFF;
+		}
+	}
+}
+
+void ASound::update1(int channelIndex) {
+	/* asound_update1: called with cx = channelIndex+1 in the original loop.
+	 * Accesses channel via the adlib_channels pointer table. */
+	AdlibChannel *ch = _channels[channelIndex];
+
+	if (ch->_freqSweepCounter == 0)
+		return;
+
+	_anySweepActive = 1;
+	ch->_freqAccum += ch->_freqStep;
+	ch->_freqSweepCounter--;
+
+	if (ch->_freqSweepCounter != 0)
+		return;
+
+	/* Sweep finished: write zero frequency to silence this OPL voice. */
+	uint8 voice = (uint8)channelIndex;
+	write((uint8)(voice + 0xA0), 0x00);
+	write((uint8)(voice + 0xB0), 0x00);
+}
+
+void ASound::setFrequency(uint8 voice, uint16 freq) {
+	/* asound_setFrequency: writes A0+v and B0+v (with key-on). */
+	write((uint8)(voice + 0xA0), (uint8)(freq & 0xFF));
+	uint8 bVal = (_adlibPorts[voice + 0xB0] & 0x20)
+		| (uint8)((freq >> 8) & 0x03)
+		| 0x20;
+	write((uint8)(voice + 0xB0), bVal);
+}
+
+void ASound::noise_inner(int channelIndex) {
+	/* adlib_noise_inner */
+	AdlibChannel *ch = _channels[channelIndex];
+
+	if (ch->_freqSweepCounter == 0)
+		return;
+
+	uint16 rnd = getRandomNumber();
+	uint16 freq = (rnd & ch->_noiseFreqMask) + ch->_freqAccum;
+	setFrequency((uint8)channelIndex, freq);
+}
+
+void ASound::updateAllChannels() {
+	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+		_channels[i]->processChannelFade();
+}
+
+void ASound::findFreeChannel(byte *soundData) {
+	_findChannelMode = 0;
+
+	/* Scan channels 0-6 for an empty slot. */
+	for (int ch = 0; ch < 7; ++ch) {
+		if (_channels[ch]->_activeCount == 0) {
+			_channels[ch]->load(soundData);
+			return;
+		}
+	}
+
+	/* _findChannelMode==0: fall through to full search. */
+	findFreeChannelFull(soundData);
+}
+
+void ASound::findFreeChannelFull(byte *soundData) {
+	_findChannelMode = 2;
+
+	/* Scan channels 7-8 for an empty slot. */
+	if (_channel7._activeCount == 0) {
+		_channel7.load(soundData); return;
+	}
+	if (_channel8._activeCount == 0) {
+		_channel8.load(soundData); return;
+	}
+
+	/* Scan ch8 then ch7 for pending-stop (pre-emptible). */
+	if (_channel8._pendingStop == 0xFF) {
+		_channel8.load(soundData); return;
+	}
+	if (_channel7._pendingStop == 0xFF) {
+		_channel7.load(soundData); return;
+	}
+
+	/* If _findChannelMode allows it, scan ch6..ch0 for pending-stop. */
+	if (_findChannelMode == 0) {
+		if (_channel6._pendingStop == 0xFF) {
+			_channel6.load(soundData); return;
+		}
+		if (_channel5._pendingStop == 0xFF) {
+			_channel5.load(soundData); return;
+		}
+		if (_channel4._pendingStop == 0xFF) {
+			_channel4.load(soundData); return;
+		}
+		if (_channel3._pendingStop == 0xFF) {
+			_channel3.load(soundData); return;
+		}
+		if (_channel2._pendingStop == 0xFF) {
+			_channel2.load(soundData); return;
+		}
+		if (_channel1._pendingStop == 0xFF) {
+			_channel1.load(soundData); return;
+		}
+		if (_channel0._pendingStop == 0xFF) {
+			_channel0.load(soundData); return;
+		}
+	}
+}
+
+int ASound::isMusicChannelsActive() {
+	_musicOnlyFlag = 1;
+	uint8 result = 0;
+	for (int i = 0; i <= 6; ++i)
+		result |= _channels[i]->_activeCount;
+	return result;
+}
+
+int ASound::isAnyChannelActive() {
+	_musicOnlyFlag = 0;
+	uint8 result = 0;
+	for (int i = 0; i < ADLIB_CHANNEL_COUNT; ++i)
+		result |= _channels[i]->_activeCount;
+	return result;
+}
+
+void ASound::pollAllChannels() {
+	_activeChannelNumber = 0;
+	for (int ch = 0; ch < ADLIB_CHANNEL_COUNT; ++ch) {
+		_activeChannelPtr = _channels[ch];
+		pollActiveChannel();
+		/* Note: _activeChannelNumber is incremented at the end of
+		 * pollActiveChannel, not here. */
+	}
+}
+
+bool ASound::isSoundActive(byte *ptr) const {
+	for (int ch = 0; ch < ADLIB_CHANNEL_COUNT; ++ch) {
+		if (_channels[ch]->_activeCount && _channels[ch]->_soundData == ptr)
+			return true;
+	}
+	return false;
+}
+
+uint16 ASound::readWord_impl() {
+	/* asound_readWord: advance pSrc past the low byte, read lo then hi. */
+	uint16 lo = *++pSrc;
+	uint16 hi = *++pSrc;
+	return lo | (hi << 8);
+}
+
+void ASound::pollActiveChannel() {
+	AdlibChannel *ch = _activeChannelPtr;
+
+	if (ch->_activeCount == 0) {
+		++_activeChannelNumber;
+		return;
+	}
+
+	/* byte_16A0A: volume-dirty flag.  Cleared here, set by various opcodes
+	 * and by the fade/vibrato sections; causes writeVolume at the end. */
+	bool volDirty = false;
+
+	/* ---- Key-on delay countdown ---- */
+	if (ch->_keyOnDelay != 0) {
+		ch->_keyOnDelay--;
+		ch = _activeChannelPtr;
+		if (ch->_keyOnDelay == 0)
+			updateOctave();
+	}
+
+	/* ---- Duration countdown ---- */
+	ch = _activeChannelPtr;
+	ch->_activeCount--;
+	ch = _activeChannelPtr;
+	if (ch->_activeCount != 0)
+		goto post_keyon;
+
+	/* ================================================================
+	 * Command-dispatch loop.
+	 * Re-entered (via goto dispatch) after each command that does not
+	 * consume a duration tick.  Falls through to the note-event path
+	 * when a note byte is encountered.
+	 * ================================================================ */
+dispatch:
+	{
+		ch = _activeChannelPtr;
+		pSrc = ch->_pSrc;
+
+		uint8 b = *pSrc;
+
+		if (!(b & 0x80))
+			goto note_event;
+
+		/* Decode group (si) and sub-opcode (di). */
+		uint8 si = b & 0x70;
+		uint8 di = b & 0x0F;
+
+		if (si == 0x00) {
+			/* ============================================================
+			 * opcodes1  (group 0, 0x8_)
+			 * Called with: pSrc pointing at the command byte.
+			 * On entry, opcodes1 does: inc pSrc (skip past command byte).
+			 * On exit, sets _pSrc = pSrc + 1 (def_10CDC).
+			 * ============================================================ */
+			pSrc++;   /* skip command byte */
+
+			switch (di) {
+
+			case 0x0: /* set sampleIndex, call loadSample */
+				ch->_sampleIndex = *pSrc;
+				loadSample();
+				volDirty = true;
+				/* def_10CDC: _pSrc = pSrc + 1 */
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+
+			case 0x1: /* set velocity */
+			{
+				uint8 vel = *pSrc;
+				if (ch->_pendingStop != 0) {
+					/* If current velocity (signed) > new vel (signed), clamp down. */
+					if ((int16)(int8)ch->_velocity > (int16)(int8)vel)
+						ch->_velocity = vel;
+				} else {
+					ch->_velocity = vel;
+				}
+				volDirty = true;
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+			}
+
+			case 0x2: /* set volume */
+			{
+				uint8 vol = *pSrc;
+				if (ch->_pendingStop != 0) {
+					/* Don't raise volume above current when fading out. */
+					uint16 curVol = (uint16)ch->_volume;
+					if (curVol <= (uint16)vol)
+						goto op2_set_vol;
+					/* else fall through and skip the set */
+					volDirty = true;
+					ch->_pSrc = pSrc + 1;
+					goto dispatch;
+				}
+op2_set_vol:
+				ch->_volume = vol;
+				volDirty = true;
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+			}
+
+			case 0x3: /* set patchAttenuation */
+				ch->_patchAttenuation = *pSrc;
+				volDirty = true;   /* opcodes1 case 3 jumps to loc_10B56 -> byte_16A0A=1 */
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+
+			case 0x4: /* set fade: fadePeriodReload, volumeFadeStep */
+				if (ch->_pendingStop != 0) {
+					/* Skip this opcode entirely when pending-stop is set. */
+					ch->_pSrc = pSrc + 2;
+					goto dispatch;
+				}
+				ch->_fadePeriodReload = *pSrc++;
+				ch->_volumeFadeStep = *pSrc;
+				ch->_fadePeriodCounter = 1;
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+
+			case 0x5: /* set vibrato: vibPeriodReload, vibPeriodCounter=reload, vibratoDepth, vibratoMode */
+				ch->_vibPeriodReload = *pSrc++;
+				ch->_vibPeriodCounter = ch->_vibPeriodReload;
+				ch->_vibratoDepth = *pSrc++;
+				ch->_vibratoMode = *pSrc;
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+
+			case 0x6: /* set transpose */
+				ch->_transpose = *pSrc;
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+
+			case 0x7: /* set octaveTranspose */
+				ch->_octaveTranspose = *pSrc;
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+
+			case 0x8: /* set field_11 (_writeVolumePending) */
+				ch->_writeVolumePending = *pSrc;
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+
+			case 0x9: /* set arpeggio: field_12->field_C, pitchBend, field_13 */
+				ch->_arpPeriodReload = *pSrc++;
+				ch->_arpPeriodCounter = ch->_arpPeriodReload;   /* field_C = field_12 */
+				ch->_pitchBend = *pSrc++;
+				ch->_arpCounterReload = *pSrc;
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+
+			case 0xA: /* skip forward by N+3 bytes (from the command byte) */
+			{
+				uint8 skip = *pSrc;
+				/* pSrc currently points at the byte after the command byte.
+				 * Advance _pSrc by (skip + 3) from the *command* byte position,
+				 * which is pSrc-1.  So _pSrc += skip + 3 from original _pSrc,
+				 * which was command byte.  But def_10CDC does _pSrc = pSrc+1
+				 * giving +2 past command.  Then skip+3 total from command: */
+				 /* asm: si = skip; ax = si + 3; add pSrc, ax; then def_10CDC
+				  * does pSrc+1.  So net advance from the command byte =
+				  * skip + 3 + 1 = skip + 4.  But pSrc already points one past
+				  * command byte, so _pSrc = pSrc + skip + 3. */
+				ch->_pSrc = pSrc + (uint16)skip + 3;
+				goto dispatch;
+			}
+
+			case 0xB: /* set durationOverride, clear noteOffset */
+				ch->_durationOverride = *pSrc;
+				ch->_noteOffset = 0;
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+
+			case 0xC: /* set noteOffset, clear durationOverride */
+				ch->_noteOffset = *pSrc;
+				ch->_durationOverride = 0;
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+
+			default:
+				/* Unknown sub-opcode: advance past the single operand byte. */
+				ch->_pSrc = pSrc + 1;
+				goto dispatch;
+			}
+		}
+
+		if (si == 0x10) {
+			/* ============================================================
+			 * opcodes2  (group 1, 0x9_)
+			 * Loop control, restart, branch / call.
+			 * On exit: either sets _pSrc directly or falls to def_10EAC
+			 * (which just returns without touching _pSrc - the handlers
+			 * themselves are responsible for updating _pSrc).
+			 * ============================================================ */
+			switch (di) {
+
+			case 0x0: /* inner loop */
+			{
+				ch = _activeChannelPtr;
+				if (ch->_innerLoopCount == 0) {
+					pSrc++;   /* advance to count byte */
+					uint8 cnt = *pSrc;
+					if (cnt == 0) {
+						ch->_pSrc += 2;
+						ch = _activeChannelPtr;
+						ch->_innerLoopPtr = ch->_pSrc;
+						ch->_innerLoopCount = 0;
+						goto dispatch;
+					}
+					ch->_innerLoopCount = (uint16)cnt;
+					/* Jump to innerLoopPtr (loc_10D5A). */
+					ch->_pSrc = ch->_innerLoopPtr;
+					goto dispatch;
+				}
+				/* Count is non-zero: decrement. */
+				ch->_innerLoopCount--;
+				ch = _activeChannelPtr;
+				if (ch->_innerLoopCount == 0) {
+					/* Loop done: advance past the two-byte opcode. */
+					ch->_pSrc += 2;
+					ch = _activeChannelPtr;
+					ch->_innerLoopPtr = ch->_pSrc;
+					goto dispatch;
+				}
+				/* Still looping: jump back. */
+				ch->_pSrc = ch->_innerLoopPtr;
+				goto dispatch;
+			}
+
+			case 0x1: /* outer loop */
+			{
+				ch = _activeChannelPtr;
+				if (ch->_outerLoopCount == 0) {
+					pSrc++;
+					uint8 cnt = *pSrc;
+					if (cnt == 0) {
+						ch->_pSrc += 2;
+						ch = _activeChannelPtr;
+						ch->_outerLoopPtr = ch->_pSrc;
+						ch->_innerLoopPtr = ch->_pSrc;
+						ch->_innerLoopCount = 0;
+						ch->_outerLoopCount = 0;
+						goto dispatch;
+					}
+					ch->_outerLoopCount = (uint16)cnt;
+					/* Jump to outerLoopPtr. */
+					ch->_pSrc = ch->_outerLoopPtr;
+					ch = _activeChannelPtr;
+					ch->_innerLoopPtr = ch->_outerLoopPtr;
+					goto dispatch;
+				}
+				/* Count non-zero: decrement. */
+				ch->_outerLoopCount--;
+				ch = _activeChannelPtr;
+				if (ch->_outerLoopCount == 0) {
+					ch->_pSrc += 2;
+					ch = _activeChannelPtr;
+					ch->_outerLoopPtr = ch->_pSrc;
+					ch->_innerLoopPtr = ch->_pSrc;
+					goto dispatch;
+				}
+				ch->_pSrc = ch->_outerLoopPtr;
+				ch = _activeChannelPtr;
+				ch->_innerLoopPtr = ch->_outerLoopPtr;
+				goto dispatch;
+			}
+
+			case 0x2: /* restart: reset all loop pointers to _soundData */
+			{
+				ch = _activeChannelPtr;
+				uint16 sd = (uint16)(uintptr_t)ch->_soundData;
+				ch->_loopStartPtr = ch->_soundData;
+				ch->_pSrc = ch->_soundData;
+				ch->_innerLoopPtr = ch->_soundData;
+				ch->_outerLoopPtr = ch->_soundData;
+				(void)sd;
+				goto dispatch;
+			}
+
+			case 0x3: /* set sound-data pointer (abs word offset), reset all loops */
+			{
+				byte *ptr = &_soundData[readWord_impl()];
+				ch = _activeChannelPtr;
+				ch->_loopStartPtr = ptr;
+				ch->_pSrc = ptr;
+				ch->_innerLoopPtr = ptr;
+				ch->_outerLoopPtr = ptr;
+				ch->_soundData = ptr;
+				goto dispatch;
+			}
+
+			case 0x4: /* branch unconditional */
+			{
+				byte *dest = &_soundData[readWord_impl()];
+				ch = _activeChannelPtr;
+				ch->_pSrc = dest;
+				goto dispatch;
+			}
+
+			case 0x5: /* branch with return-address save */
+			{
+				byte *dest = &_soundData[readWord_impl()];
+				ch = _activeChannelPtr;
+				ch->_branchTargetPtr = ch->_pSrc + 3;
+				ch->_pSrc = dest;
+				goto dispatch;
+			}
+
+			case 0x6: /* return from branch */
+			{
+				ch = _activeChannelPtr;
+				if (ch->_branchTargetPtr != nullptr) {
+					ch->_pSrc = ch->_branchTargetPtr;
+					ch->_branchTargetPtr = nullptr;
+				} else {
+					ch->_pSrc++;
+				}
+				goto dispatch;
+			}
+
+			default:
+				/* Unknown sub-opcode: no _pSrc advance (def_10EAC). */
+				goto dispatch;
+			}
+		}
+
+		if (si == 0x20) {
+			/* ============================================================
+			 * opcodes3  (group 2, 0xA_)
+			 * Tempo/callback globals, random table operations,
+			 * indirect-table operations, call-by-address, nullsub.
+			 *
+			 * opcodes3 receives di (sub-opcode) but NOT the already-
+			 * incremented pSrc; each case increments pSrc itself as needed.
+			 * ============================================================ */
+			switch (di) {
+
+			case 0x0: /* random pick from packed table -> write to stream */
+			{
+				/* Format: [cmd] [tblSize] [entry0..N-1] [targetSlot]
+				 * pSrc points at the command byte on entry. */
+				pSrc++;
+				uint8  tblSize = *pSrc;
+				pSrc++;   /* now at entry[0] */
+				(void)getRandomNumber();
+				uint16 rnd = _randomSeed & 0x7FFF;
+				int16  idx = (int16)((int16)rnd % (int16)(uint16)tblSize);
+				uint8  chosen = *(pSrc + idx);
+				uint8  target = *(pSrc + tblSize);
+				*(pSrc + (uint16)target + 1) = chosen;
+				ch = _activeChannelPtr;
+				ch->_pSrc += (uint16)tblSize + 3;
+				goto dispatch;
+			}
+
+			case 0x1: /* random range [lo..hi] -> write to stream */
+			{
+				/* Format: [cmd] [lo] [hi] [targetSlot] ... */
+				pSrc++;
+				uint8 lo = *pSrc++;
+				uint8 hi = *pSrc++;
+				uint16 range = (uint16)(hi - lo) + 1;
+				/* pSrc now at targetSlot byte. */
+				(void)getRandomNumber();
+				uint16 rnd = _randomSeed & 0x7FFF;
+				uint8  res = (uint8)((int16)rnd % (int16)range) + lo;
+				uint8  slot = *pSrc;
+				*(pSrc + (uint16)slot + 1) = res;
+				ch = _activeChannelPtr;
+				ch->_pSrc += 4;   /* command+lo+hi+slot = 4 bytes consumed */
+				goto dispatch;
+			}
+
+			case 0x2: /* indirect table read from scriptVar -> write to stream */
+			{
+				/* Format: [cmd] [varIdx] [tableSlot] [table...] */
+				pSrc++;
+				uint8 varIdx = *pSrc++;
+				uint8 tableSlot = *pSrc++;
+				/* pSrc now points at the inline table. */
+				uint8 idxVal = _scriptVars[varIdx];
+				uint8 chosen = *(pSrc + idxVal);
+				uint8 target = *(pSrc + tableSlot);
+				*(pSrc + (uint16)target + 1) = chosen;
+				ch = _activeChannelPtr;
+				ch->_pSrc += (uint16)tableSlot + 4;
+				goto dispatch;
+			}
+
+			case 0x3: /* call function by address (near call in original) */
+			{
+				uint16 fnOffset = readWord_impl();
+				callFunction(fnOffset);
+				ch = _activeChannelPtr;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			case 0x4: /* nullsub_1 with byte arg */
+			{
+				/* "call near ptr aAsoundDriverAn+33h" - the target is a no-op. */
+				pSrc++;
+				/* (void)*pSrc; */
+				ch = _activeChannelPtr;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			case 0x5: /* advance _pSrc by 4 (from command byte) */
+			{
+				/* loc_10F55 is shared with case 1's epilogue: _pSrc += 4. */
+				ch = _activeChannelPtr;
+				ch->_pSrc += 4;
+				goto dispatch;
+			}
+
+			case 0x6: /* set word_124F2 (_tempoFineStep) */
+			{
+				pSrc++;
+				uint8 val = *pSrc;
+				_tempoFineStep = (uint16)val;
+				ch = _activeChannelPtr;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			case 0x7: /* set word_124F0 (_tempoCoarseStep) */
+			{
+				pSrc++;
+				uint8 val = *pSrc;
+				_tempoCoarseStep = (uint16)val;
+				ch = _activeChannelPtr;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			case 0x8: /* set word_124EE (_tempoPeriod), enable tick callback */
+			{
+				uint16 period = readWord_impl();
+				_tempoPeriod = period;
+				ch = _activeChannelPtr;
+				ch->_pSrc += 3;
+				_tempoEnabled = 1;
+				_tempoTickCounter = 1;
+				goto dispatch;
+			}
+
+			case 0x9: /* set word_124F4 (_tempoShift) */
+			{
+				pSrc++;
+				uint8 val = *pSrc;
+				_tempoShift = (uint16)val;
+				ch = _activeChannelPtr;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			default:
+				goto dispatch;
+			}
+		}
+
+		if (si == 0x30) {
+			/* ============================================================
+			 * opcodes4  (group 3, 0xB_)
+			 * Script-variable load (imm or var), store to stream, inc, dec.
+			 * Each case increments pSrc itself, then sets _pSrc.
+			 *
+			 * Switch on di (0-4); di >= 5 falls through to return.
+			 * ============================================================ */
+			switch (di) {
+
+			case 0x0: /* var[dst] = imm */
+			{
+				pSrc++;
+				uint8 dst = *pSrc++;
+				uint8 imm = *pSrc;
+				_scriptVars[dst] = imm;
+				ch = _activeChannelPtr;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			case 0x1: /* var[dst] = var[src] */
+			{
+				pSrc++;
+				uint8 dst = *pSrc++;
+				uint8 src = *pSrc;
+				_scriptVars[dst] = _scriptVars[src];
+				ch = _activeChannelPtr;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			case 0x2: /* stream[tableSlot+1] = var[src] */
+			{
+				/* asm: [bx+di+1] = cl, where bx=pSrc, di=tableSlot. */
+				pSrc++;
+				uint8 src = *pSrc++;
+				uint8 tableSlot = *pSrc;
+				*(pSrc + (uint16)tableSlot + 1) = _scriptVars[src];
+				ch = _activeChannelPtr;
+				ch->_pSrc += 3;
+				goto dispatch;
+			}
+
+			case 0x3: /* var[dst]++ */
+			{
+				pSrc++;
+				uint8 dst = *pSrc;
+				_scriptVars[dst]++;
+				ch = _activeChannelPtr;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			case 0x4: /* var[dst]-- */
+			{
+				pSrc++;
+				uint8 dst = *pSrc;
+				_scriptVars[dst]--;
+				ch = _activeChannelPtr;
+				ch->_pSrc += 2;
+				goto dispatch;
+			}
+
+			default:
+				goto dispatch;
+			}
+		}
+
+		if (si == 0x40) {
+			/* ============================================================
+			 * opcodes5  (group 4, 0xC_)
+			 * Script-variable ALU: add/sub/mul/div/rem/and/or/xor.
+			 *
+			 * Entry: inc pSrc, read dst (si), inc pSrc, read src (di).
+			 * Then _pSrc += 3 (command + dst + src).
+			 * 16 sub-opcodes (0-15).
+			 *
+			 * For sub-opcodes 0,2,4: src is an immediate (di from the byte).
+			 * For sub-opcodes 1,3,5: src is _scriptVars[di].
+			 * For 6-9: signed or unsigned division/remainder.
+			 * For 10-15: bitwise AND/OR/XOR, imm or var.
+			 * ============================================================ */
+			pSrc++;
+			uint8 dst = *pSrc++;
+			uint8 src = *pSrc;   /* immediate value or var index */
+			ch = _activeChannelPtr;
+			ch->_pSrc += 3;
+
+			switch (di) {
+			case 0x0: _scriptVars[dst] += src;                                       break;
+			case 0x1: _scriptVars[dst] += _scriptVars[src];                          break;
+			case 0x2: _scriptVars[dst] -= src;                                       break;
+			case 0x3: _scriptVars[dst] -= _scriptVars[src];                          break;
+			case 0x4: /* mul ax=di, mul [si] -> al stored */
+				_scriptVars[dst] = (uint8)((uint16)src * (uint16)_scriptVars[dst]);  break;
+			case 0x5:
+				_scriptVars[dst] = (uint8)((uint16)_scriptVars[src] * (uint16)_scriptVars[dst]); break;
+			case 0x6: /* signed idiv by imm -> quotient in al */
+				_scriptVars[dst] = (uint8)((int8)_scriptVars[dst] / (int8)src);      break;
+			case 0x7: /* unsigned div by var -> quotient in al */
+				_scriptVars[dst] = _scriptVars[dst] / _scriptVars[src];              break;
+			case 0x8: /* signed idiv by imm -> remainder in dl (stored to dst) */
+				_scriptVars[dst] = (uint8)((int8)_scriptVars[dst] % (int8)src);      break;
+			case 0x9: /* unsigned div by var -> remainder in ah (stored to dst) */
+				_scriptVars[dst] = _scriptVars[dst] % _scriptVars[src];              break;
+			case 0xA: _scriptVars[dst] &= src;                                       break;
+			case 0xB: _scriptVars[dst] &= _scriptVars[src];                          break;
+			case 0xC: _scriptVars[dst] |= src;                                       break;
+			case 0xD: _scriptVars[dst] |= _scriptVars[src];                          break;
+			case 0xE: _scriptVars[dst] ^= src;                                       break;
+			case 0xF: _scriptVars[dst] ^= _scriptVars[src];                          break;
+			default: break;
+			}
+			goto dispatch;
+		}
+
+		if (si == 0x50) {
+			/* ============================================================
+			 * opcodes6  (group 5, 0xD_)
+			 * Conditional branch: compare var[si] vs immediate (di as imm byte).
+			 *
+			 * Format: [cmd] [varIdx] [imm] [addrLo] [addrHi]
+			 * Entry: inc pSrc, read varIdx (si reg), inc pSrc, read imm (di reg).
+			 * Comparison: var[varIdx] vs imm.
+			 *
+			 * If condition met: call asound_readWord (reads addrLo/addrHi
+			 *   from current pSrc position), set _pSrc = &_soundData[addr].
+			 * If not met: _pSrc += 5 (command+varIdx+imm+addr word = 5 bytes).
+			 *
+			 * var_2 is used as a flag: 0 = branch taken, 1 = not taken.
+			 * (Confusingly, var_2 = 1 means "taken" in opcodes7 but
+			 *  0 means "taken" in opcodes6 - see def_11292 / def_11360 difference.)
+			 *
+			 * opcodes6: var_2=1 -> taken (call readWord, set pSrc),
+			 *            var_2=0 -> not taken (_pSrc += 5).
+			 * ============================================================ */
+			pSrc++;
+			uint8 varIdx = *pSrc++;
+			uint8 imm = *pSrc;
+			uint8 vval = _scriptVars[varIdx];
+			bool  taken = false;
+
+			switch (di) {
+			case 0x0: taken = ((uint16)vval == (uint16)imm);                    break; /* == */
+			case 0x1: taken = ((uint16)vval != (uint16)imm);                    break; /* != */
+			case 0x2: taken = ((int16)(uint16)vval >= (int16)(uint16)imm);      break; /* jge -> taken if >= */
+			case 0x3: taken = ((int16)(uint16)vval <= (int16)(uint16)imm);      break; /* jle -> taken if <= */
+			case 0x4: taken = (_scriptVars[varIdx] != _scriptVars[imm]);        break; /* jnz after cmp [di],[si] */
+			case 0x5: taken = (_scriptVars[varIdx] == _scriptVars[imm]);        break; /* jz  after cmp [di],[si] */
+			case 0x6: taken = (_scriptVars[imm] <= _scriptVars[varIdx]);        break; /* jbe -> [di]<=[si] taken */
+			case 0x7: taken = (_scriptVars[imm] >= _scriptVars[varIdx]);        break; /* jnb -> [di]>=[si] taken */
+			default:  break;
+			}
+
+			ch = _activeChannelPtr;
+			if (taken) {
+				ch->_pSrc = &_soundData[readWord_impl()];
+			} else {
+				ch->_pSrc += 5;
+			}
+			goto dispatch;
+		}
+
+		if (si == 0x60) {
+			/* ============================================================
+			 * opcodes7  (group 6, 0xE_)
+			 * Conditional branch: compare var[si] vs var[di] (both scriptVars).
+			 *
+			 * Format: [cmd] [varIdxA] [varIdxB] [addrLo] [addrHi]
+			 * Entry: inc pSrc, read varIdxA (si), inc pSrc, read varIdxB (di).
+			 * var_2 = 0 initially.
+			 *
+			 * If condition met: var_2 = 1 -> branch taken:
+			 *   _pSrc = pSrc + 5; _branchTargetPtr = pSrc + 5;
+			 *   call readWord -> _pSrc = &_soundData[addr].
+			 * If not met (var_2 = 0): _pSrc += 5 (skip condition + word).
+			 *
+			 * Note: unlike opcodes6, opcodes7 DOES save a branch-target
+			 * (_branchTargetPtr = _pSrc + 5) on the taken path.
+			 * ============================================================ */
+			pSrc++;
+			uint8 idxA = *pSrc++;
+			uint8 idxB = *pSrc;
+			uint8 va = _scriptVars[idxA];
+			uint8 vb = (uint8)idxB;   /* di byte used as an immediate in some cases... */
+
+			/* Actually in opcodes7 the layout is var vs var:
+			 * si = varIdxA, di = varIdxB (both are variable indices). */
+			vb = _scriptVars[idxB];
+
+			bool taken = false;
+			switch (di & 0x07) {
+			case 0x0: taken = ((uint16)va != (uint16)vb);  break; /* jnz after cmp ax,di (case 0: jz -> NOT taken if ==; var_2 stays 0; but then def path: var_2==0 -> skip. Wait - let me re-read.) */
+				/* Re-reading loc_112F0: cmp ax,di; jz loc_11352 (-> var_2=1=taken).
+				 * So case 0: taken = (va == vb). */
+			default: break;
+			}
+
+			/* Actually re-reading carefully:
+			 * case 0 (loc_112F0): cmp ax,di; jz -> loc_11352 (var_2=1, taken)
+			 *                      else -> loc_112FA (ax=0, var_2=0, not taken)
+			 * case 1 (loc_11302): cmp ax,di; jz -> loc_1130A -> (jz loc_112FA, not taken)
+			 *                     else -> loc_11284 (var_2=1, taken)
+			 * -> case 1: taken = (va != vb)
+			 * case 2 (loc_1130E): jge -> loc_112FA (not taken); else loc_11284 (taken)
+			 * -> taken = (va < vb) (signed)
+			 * case 3 (loc_1131A): jle -> not taken; else taken
+			 * -> taken = (va > vb) (signed)
+			 * case 4 (loc_11326): cmp [di],al; jnz -> loc_112FA (not taken); else loc_11284 (taken)
+			 * -> taken = (_scriptVars[idxB] == va) (already same as case 0 with vars swapped)
+			 * case 5 (loc_11332): cmp [di],al; jz->loc_1130A (not taken if ==, taken if !=)
+			 * -> taken = (_scriptVars[idxB] != va)
+			 * case 6 (loc_1133C): jbe -> not taken; else taken -> taken = (_scriptVars[idxB] > va)
+			 * case 7 (loc_11348): jnb -> not taken; else taken -> taken = (_scriptVars[idxB] < va)
+			 */
+			switch (di) {
+			case 0x0: taken = (va == vb);               break;
+			case 0x1: taken = (va != vb);               break;
+			case 0x2: taken = ((int8)va < (int8)vb);   break;
+			case 0x3: taken = ((int8)va > (int8)vb);   break;
+			case 0x4: taken = (vb == va);               break;
+			case 0x5: taken = (vb != va);               break;
+			case 0x6: taken = (vb > va);                break;
+			case 0x7: taken = (vb < va);                break;
+			default:  break;
+			}
+
+			ch = _activeChannelPtr;
+			if (taken) {
+				/* Save return target (pSrc+5 from the command byte). */
+				ch->_branchTargetPtr = ch->_pSrc + 5;
+				ch->_pSrc = &_soundData[readWord_impl()];
+			} else {
+				ch->_pSrc += 5;
+			}
+			goto dispatch;
+		}
+
+		/* Unknown group: skip. */
+		goto dispatch;
+	}
+
+note_event:
+	{
+		/* ---- Note event: [note][duration] ---- */
+		ch = _activeChannelPtr;
+		pSrc = ch->_pSrc;
+		ch->_note = *pSrc;
+		pSrc++;
+		ch->_activeCount = *pSrc;
+		pSrc++;
+		ch->_pSrc += 2;
+
+		ch = _activeChannelPtr;
+		if (ch->_note == 0 || ch->_activeCount == 0) {
+			updateOctave();
+			goto post_keyon;
+		}
+
+		/* ---- Key-on ---- */
+		ch = _activeChannelPtr;
+		if (ch->_durationOverride != 0)
+			ch->_keyOnDelay = ch->_durationOverride;
+		else
+			ch->_keyOnDelay = ch->_activeCount - ch->_noteOffset;
+		noteOn();
+	}
+
+post_keyon:
+	{
+		ch = _activeChannelPtr;
+
+		/* ---- Arpeggio counter (field_13 / _arpCounterReload) ---- */
+		if (ch->_arpCounterReload != 0) {
+			/* Decrement the arpeggio period counter (field_C / _arpPeriodCounter). */
+			ch->_arpPeriodCounter--;
+			ch = _activeChannelPtr;
+			if (ch->_arpPeriodCounter == 0) {
+				/* Reload from field_12 (_arpPeriodReload). */
+				ch->_arpPeriodCounter = ch->_arpPeriodReload;
+				/* Call sub_117E8 (writeArpeggio - writes the arpeggio frequency). */
+				writeArpeggio();
+			}
+			ch = _activeChannelPtr;
+			/* Decrement the repeat counter unless it is the infinite sentinel 0xFF. */
+			if (ch->_arpCounterReload != 0xFF)
+				ch->_arpCounterReload--;
+		}
+
+		/* ---- Write-volume pending (field_11 / _writeVolumePending) ---- */
+		ch = _activeChannelPtr;
+		if (ch->_writeVolumePending != 0) {
+			/* sub_11856 was already called by writeArpeggio above (or this is
+			 * a standalone field_11 set via opcode 8).  Clear the flag. */
+			writeArpeggio();
+			ch = _activeChannelPtr;
+			ch->_writeVolumePending = 0;
+		}
+
+		/* ---- Fade / volume step ---- */
+		ch = _activeChannelPtr;
+		if (ch->_fadePeriodCounter != 0) {
+			ch->_fadePeriodCounter--;
+			ch = _activeChannelPtr;
+			if (ch->_fadePeriodCounter == 0) {
+				ch->_fadePeriodCounter = ch->_fadePeriodReload;
+				ch = _activeChannelPtr;
+				if (ch->_volumeFadeStep != 0) {
+					if (ch->_pendingStop != 0) {
+						/* Pending-stop fade: step velocity and volume down. */
+						if (ch->_velocity > 0)
+							ch->_velocity += ch->_volumeFadeStep;
+						ch = _activeChannelPtr;
+						if (ch->_volume != 0)
+							ch->_volume += ch->_volumeFadeStep;
+					} else {
+						/* Normal fade: step velocity, clamp to [0, 0x7F]. */
+						if ((int8)ch->_volumeFadeStep > 0) {
+							ch->_velocity += ch->_volumeFadeStep;
+							ch = _activeChannelPtr;
+							if ((uint16)ch->_velocity > 0x7F)
+								ch->_velocity = 0x7F;
+						} else {
+							ch->_velocity += ch->_volumeFadeStep;
+							ch = _activeChannelPtr;
+							if ((int8)ch->_velocity < 0)
+								ch->_velocity = 0;
+						}
+					}
+					volDirty = true;   /* byte_16A0A = 1 */
+				}
+			}
+		}
+
+		/* ---- Vibrato ---- */
+		ch = _activeChannelPtr;
+		if (ch->_vibPeriodCounter != 0) {
+			ch->_vibPeriodCounter--;
+			ch = _activeChannelPtr;
+			if (ch->_vibPeriodCounter == 0) {
+				ch->_vibPeriodCounter = ch->_vibPeriodReload;
+				ch = _activeChannelPtr;
+
+				if ((int8)ch->_vibratoDepth > 0) {
+					/* Positive vibrato: ceiling at 0x7F. */
+					uint8 sum = ch->_vibratoDepth + ch->_patchAttenuation;
+					if (sum > 0x7F) {
+						if (ch->_vibratoMode == 0xFF) {
+							/* One-shot: clamp and stop. */
+							ch->_vibratoDepth = 0;
+							ch->_patchAttenuation = 0x7F;
+						} else if (sum == 0x80 && ch->_vibratoDepth != 1) {
+							/* Soft bounce at 0x80: patchAtten = 0x7F - vibratoDepth. */
+							ch->_patchAttenuation = 0x7F - ch->_vibratoDepth;
+						} else {
+							ch->_vibratoDepth = (uint8)(-(int8)ch->_vibratoDepth);
+						}
+					}
+					ch = _activeChannelPtr;
+					ch->_patchAttenuation += ch->_vibratoDepth;
+					volDirty = true;
+
+				} else if ((int8)ch->_vibratoDepth < 0) {
+					/* Negative vibrato: floor at 0. */
+					uint8 sum = ch->_vibratoDepth + ch->_patchAttenuation;
+					/* Note: sum is uint8 wrapping, but the asm uses "or al,al; jge"
+					 * treating the result as signed. */
+					if ((int8)sum < 0) {
+						if (ch->_vibratoMode == 0xFF) {
+							ch->_vibratoDepth = 0;
+							ch->_patchAttenuation = 0;
+						} else if (sum == 0xFF && ch->_vibratoDepth != 0xFF) {
+							/* Soft bounce: patchAtten = neg(vibratoDepth). */
+							ch->_patchAttenuation = (uint8)(-(int8)ch->_vibratoDepth);
+						} else {
+							ch->_vibratoDepth = (uint8)(-(int8)ch->_vibratoDepth);
+						}
+					}
+					ch = _activeChannelPtr;
+					ch->_patchAttenuation += ch->_vibratoDepth;
+					volDirty = true;
+				}
+				/* vibratoDepth == 0: nothing to do. */
+			}
+		}
+
+		if (volDirty)
+			writeVolume();
+	}
+
+	++_activeChannelNumber;
+}
+
+void ASound::playSound(int offset) {
+	findFreeChannelFull(loadData(offset));
+}
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/asound.h b/engines/mads/madsv2/forest/asound.h
new file mode 100644
index 00000000000..32fefa08461
--- /dev/null
+++ b/engines/mads/madsv2/forest/asound.h
@@ -0,0 +1,649 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ASOUND_H
+#define MADS_FOREST_ASOUND_H
+
+#include "common/mutex.h"
+#include "common/queue.h"
+#include "common/util.h"
+#include "mads/core/sound_manager.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+#define ADLIB_CHANNEL_COUNT 9
+
+struct AdlibChannel {
+	static bool _isDisabled;
+
+	// ---- byte fields (offsets 0x00 - 0x13) --------------------------------
+
+	uint8  _activeCount = 0; // 0x00  duration tick countdown; 0 = channel idle
+	uint8  _pitchBend = 0; // 0x01  signed pitch-bend offset applied by writePitchBend
+	uint8  _volumeFadeStep = 0; // 0x02  signed per-period volume/velocity delta (0xFF = fade out)
+	uint8  _vibratoDepth = 0; // 0x03  signed LFO depth; negated each period to oscillate
+	uint8  _note = 0; // 0x04  MIDI note number for current event
+	uint8  _sampleIndex = 0; // 0x05  index into _samples[] selecting the OPL patch
+	uint8  _volume = 0; // 0x06  channel volume (0-127)
+	uint8  _noteOffset = 0; // 0x07  subtracted from _activeCount to shorten note duration
+	uint8  _durationOverride = 0; // 0x08  when non-zero, overrides the stream's duration byte
+	uint8  _keyOnDelay = 0; // 0x09  countdown before the OPL key-on bit is cleared (gate time)
+	uint8  _fadePeriodCounter = 0; // 0x0A  counts down to 0 before applying _volumeFadeStep
+	uint8  _fadePeriodReload = 0; // 0x0B  reload value for _fadePeriodCounter
+	uint8  _arpPeriodCounter = 0; // 0x0C  arpeggio tick counter (reloaded from _arpPeriodReload)
+	uint8  _vibPeriodCounter = 0; // 0x0D  LFO tick counter; when it reaches 0 a vibrato step fires
+	uint8  _vibPeriodReload = 0; // 0x0E  reload value for _vibPeriodCounter
+	uint8  _patchAttenuation = 0; // 0x0F  per-note attenuation offset added on top of the patch TL
+	uint8  _velocity = 0; // 0x10  note velocity (0-127); used together with _volume for TL
+	uint8  _writeVolumePending = 0; // 0x11  non-zero -> call sub_11856 (secondary freq write) this tick
+	uint8  _arpPeriodReload = 0; // 0x12  arpeggio period reload value (set by opcode 9)
+	uint8  _arpCounterReload = 0; // 0x13  arpeggio counter reload (decremented; 0xFF = infinite)
+
+	// ---- pointer fields (offsets 0x14 - 0x1F, word-sized in original) ----
+
+	byte *_loopStartPtr = nullptr; // 0x14  start of the current loop body
+	byte *_pSrc = nullptr; // 0x16  current read pointer into the sound-data stream
+	byte *_innerLoopPtr = nullptr; // 0x18  inner-loop restart address
+	byte *_outerLoopPtr = nullptr; // 0x1A  outer-loop restart address
+	byte *_soundData = nullptr; // 0x1C  base address of this channel's sound-data block
+	byte *_branchTargetPtr = nullptr; // 0x1E  target of the most recent branch/jump opcode
+
+	// ---- word counter fields (offsets 0x20 - 0x29) ----------------------
+
+	uint16 _innerLoopCount = 0; // 0x20  remaining inner-loop iterations (0 = infinite until opcode)
+	uint16 _outerLoopCount = 0; // 0x22  remaining outer-loop iterations (0 = infinite until opcode)
+	uint16 _noiseFreqMask = 0; // 0x24  AND mask applied to the random number in noise mode
+	uint16 _freqAccum = 0; // 0x26  frequency sweep accumulator (base frequency + swept offset)
+	uint16 _freqStep = 0; // 0x28  per-tick increment added to _freqAccum during a sweep
+
+	// ---- sweep / pause state (offsets 0x2A - 0x2D) ----------------------
+
+	// 0x2A-0x2B  Last computed OPL total-level bytes written by writeVolume.
+	//            lo byte (0x2A) = carrier TL nibble; hi byte (0x2B) = modulator TL nibble.
+	//            Written at the end of asound_writeVolume so that command7 (resume) can
+	//            restore operator levels without a full recalculation.
+	uint8  _cachedCarrierTL = 0; // 0x2A  cached carrier total-level (6-bit, 0 = loudest)
+	uint8  _savedFreqSweep = 0; // 0x2B  copy of _freqSweepCounter saved by command6 (pause)
+	uint8  _freqSweepCounter = 0; // 0x2C  countdown for frequency-sweep ticks; 0 = sweep done
+	uint8  _savedSweepCounter = 0; // 0x2D  second save slot: _freqSweepCounter is mirrored here
+	//       by command6/7 so command7 can restore the live value
+
+// ---- tuning / control (offsets 0x2E - 0x31) ------------------------
+
+	uint8  _transpose = 0; // 0x2E  semitone offset added when looking up _semitoneFreqTable
+	uint8  _octaveTranspose = 0; // 0x2F  octave shift: added to _note before octave calculation
+	uint8  _pendingStop = 0; // 0x30  0xFF = channel is fading out and will go idle when silent
+	uint8  _vibratoMode = 0; // 0x31  0xFF = one-shot vibrato (clamp & stop); else = continuous
+
+	/**
+	 * Zeroes the "live" fields of one channel without touching the
+	 * loop/sound-data pointers (those are handled separately by load).
+	 */
+	void reset();
+
+	/**
+	 * Clears the entire channel struct, then initialises all loop/source
+	 * pointers to the supplied sound-data address, sets _patchAttenuation to
+	 * 0x40 (maximum, silent), and marks the channel active (_activeCount = 1).
+	 */
+	void load(byte *soundData);
+
+	/**
+	 * Redirects _loopStartPtr and _pSrc to the null/silence stream and arms a
+	 * one-step volume fade (_volumeFadeStep = 0xFF, _fadePeriodReload/Counter = 1)
+	 * so the channel fades out gracefully over the next tick.
+	 */
+	void setPtr2(byte *ptr);
+
+	/**
+	 * Marks the channel pending-stop (_pendingStop = 0xFF) and redirects
+	 * _soundData to the null/silence stream.  Unlike setPtr2 this does not
+	 * touch _volumeFadeStep; the channel fades naturally via processChannelFade.
+	 */
+	void enable();
+
+	/**
+	 * Called every frame for each channel.  When _pendingStop is set, waits
+	 * until both _velocity and _volume are zero before marking the channel idle
+	 * (_activeCount = 0); otherwise applies _volumeFadeStep to velocity/volume
+	 * each _fadePeriodReload ticks and clamps the result to [0, 0x7F].
+	 */
+	void processChannelFade();
+};
+
+// ---------------------------------------------------------------------------
+// AdlibSample  (unchanged from Return of the Phantom, 0x16 bytes)
+// ---------------------------------------------------------------------------
+struct AdlibSample {
+	uint8  _attackRate = 0;
+	uint8  _decayRate = 0;
+	uint8  _sustainLevel = 0;
+	uint8  _releaseRate = 0;
+	uint8  _egTyp = 0;
+	uint8  _ksr = 0;
+	uint8  _totalLevel = 0;
+	uint8  _scalingLevel = 0;
+	uint8  _waveformSelect = 0;
+	uint8  _freqMultiple = 0;
+	uint8  _feedback = 0;
+	uint8  _ampMod = 0;
+	uint8  _vib = 0;
+	uint8  _alg = 0;
+	uint8  _freqSweepInit = 0; // initial _freqSweepCounter value; 0 = immediate key-on
+	uint8  _reserved = 0;
+	uint16 _freqMask = 0; // loaded into channel _noiseFreqMask
+	uint16 _freqBase = 0; // loaded into channel _freqAccum
+	uint16 _outerLoopPtr = 0; // loaded into channel _freqStep
+
+	AdlibSample() {
+	}
+	AdlibSample(Common::SeekableReadStream &s);
+};
+
+// ---------------------------------------------------------------------------
+// ASound  -  Dragonsphere Adlib sound driver base class
+//
+// Command dispatch table layout (from off_11A14 / off_11A26 / off_11A2E /
+// funcs_12251 / off_11A64 in the disassembly):
+//
+//   Table 1  off_11A14  commands  0- 8   (max=8,    base=0,    9 entries)
+//   Table 2  off_11A26  commands 16-19   (max=0x13, base=0x10, 4 entries)
+//             command16  = background-music dispatcher (calls command18)
+//             command17  = play specific piece (loads 7 channels direct)
+//             command18  = re-entrant music launcher (reads word_12370 to pick a sub-command)
+//             command19  = no-op (asound_command98)
+//   Table 3  off_11A2E  commands 24-32   (max=0x20, base=0x18, 9 entries)
+//   Table 4  funcs_12251 commands 32-49  (max=0x31, base=0x20, 18 entries)
+//             Includes asound_command32-48 (music pieces / SFX loaders)
+//             and asound_command98 (no-op) in the last slot.
+//   Table 5  off_11A64  commands 64-101  (max=0x65, base=0x40, 38 entries)
+//             Includes asound_command64-101 (single-shot SFX loaders via
+//             findFreeChannel / findFreeChannelFull) and two no-ops.
+//
+// The driver also exposes:
+//   asound_command90 / 91  - two-voice SFX (findFreeChannelFull x2)
+//   asound_command95       - four-voice music piece (findFreeChannel x4)
+//   sub_11F98              - two-voice SFX (findFreeChannelFull x2)
+//
+// word_12370 tracks the "current music index" used by command18 to select
+// which music-piece loader to call.
+// ---------------------------------------------------------------------------
+class ASound : public SoundDriver {
+protected:
+	/** Member-function pointer type for deferred sound-loader callbacks. */
+	typedef void (ASound::*CallbackFunction)();
+
+private:
+	// ---- callback / tick state ------------------------------------------
+	uint16 _callbackCounter = 0;  // per-tick countdown
+	uint16 _callbackPeriod = 0;  // reload value; 0 = callback disabled
+	// Pointer to the deferred sound-loader called when the
+	// counter fires (stored as uint16 in the original; typed as a member
+	// function pointer in the C++ port).
+	CallbackFunction _callbackFnPtr = nullptr;
+
+	// ---- active-channel context (set by pollAllChannels) ----------------
+	AdlibChannel *_activeChannelPtr = nullptr;
+	uint8          _activeChannelNumber = 0;
+	uint16         _activeChannelReg = 0; // 0xA0+ch register for the active channel
+	uint16         _currentOpBase = 0; // operator register base for the active channel
+
+	// ---- per-frame working state ----------------------------------------
+	AdlibSample *_samplePtr = nullptr; // patch being loaded/written
+	byte *pSrc = nullptr; // current read pointer (mirrors channel _pSrc)
+	int16          _pollResult = 0;
+	int16          _resultFlag = 0;
+	uint16         _randomSeed = 0x4D2;
+
+	// ---- driver-wide flags ----------------------------------------------
+	uint16 _isDisabled = 0; // non-zero while the engine is paused (command6)
+	uint8  _findChannelMode = 0; // 0=full search, 1=ch0-5 only, 2=ch6-8 then pending
+
+	// ---- per-channel sweep shadows (for channel 5 special-casing) -------
+	//      Not present as separate globals in Dragonsphere; the channel
+	//      struct fields _savedFreqSweep / _savedSweepCounter handle this.
+
+	// ---- misc globals ---------------------------------------------------
+	uint8  _anySweepActive = 0; // set to 1 if any channel has _freqSweepCounter > 0
+	int    _frameNumber2 = 0; // secondary frame counter incremented every update
+
+	// ---- script / sequencer registers -----------------------------------
+	uint8  _scriptVars[32] = {}; // byte_16A10: 32 general-purpose script registers
+
+	// ---- music-index tracker (word_12370) --------------------------------
+	// Tracks which music piece was last launched by command18.
+	uint16 _musicIndex = 0;
+
+	// ---- tempo / sequencer state (from opcodes3 group 2 handlers) --------
+	uint8  _musicOnlyFlag = 0;     // byte_12393: 1=music-only check, 0=all channels
+	uint16 _tempoFineStep = 0;     // word_124F2: fine tempo step (opcode A6)
+	uint16 _tempoCoarseStep = 0;   // word_124F0: coarse tempo step (opcode A7)
+	uint16 _tempoPeriod = 0;       // word_124EE: tempo period in ticks (opcode A8)
+	uint8  _tempoEnabled = 0;      // non-zero when tempo tick is active (opcode A8)
+	uint16 _tempoTickCounter = 0;  // countdown for tempo callback
+	uint16 _tempoShift = 0;        // word_124F4: tempo shift (opcode A9)
+
+	// =========================================================================
+	// Private helpers
+	// =========================================================================
+
+	/** Timer callback entry point; calls update(). */
+	void onTimer();
+
+	/** Zeros _callbackFnPtr, _callbackCounter, and _callbackPeriod. */
+	void clearCallback();
+	void resetCallback() {
+		clearCallback();
+	}
+
+	/**
+	 * Forces one OPL operator to maximum attenuation (total-level = 0x3F)
+	 * while preserving its upper KSL bits.  'portIndex' is the register index
+	 * into _adlibPorts (i.e. the 0x40-range operator register).
+	 * Used by command6 to mute all operator pairs during a pause.
+	 */
+	void adlib_channelOff(uint8 portIndex);
+
+	/**
+	 * Computes and writes the total-level registers for the active channel
+	 * (asound_writeVolume).
+	 *
+	 * Two top-level paths depending on the _alg field of the first sample:
+	 *   _alg == 0  (FM): both operators contribute; loop twice writing
+	 *              VOICE_SLOTS[ch][0] then VOICE_SLOTS[ch][1].
+	 *   _alg != 0  (additive): only the carrier (slot 1) carries volume.
+	 *
+	 * Within each pass, two inner paths based on OPL version:
+	 *   < 0x18  (OPL2): TL = clamp(0x3F - var_2, 0, 0x3F).
+	 *   >= 0x18 (OPL3): patch-attenuation-aware mapping using
+	 *              PATCH_ATTEN_TO_TL and VOL_VEL_TO_ATTEN_STEP tables;
+	 *              writes two registers (offset 0 and offset 2).
+	 *
+	 * Caches the two written TL bytes in _cachedCarrierTL / _savedFreqSweep
+	 * so command7 can restore levels without a full recalculation.
+	 */
+	void writeVolume();
+
+	/**
+	 * Derives the OPL F-number and block (octave) from _note, _octaveTranspose,
+	 * and _transpose using _semitoneFreqTable, then writes registers 0xA0+ch
+	 * and 0xB0+ch (with key-on bit set).
+	 */
+	void writeFrequency();
+
+	/**
+	 * Applies _pitchBend as a signed offset to the already-computed frequency
+	 * registers without a full note recalculation.
+	 */
+	void writePitchBend();
+
+	/**
+	 * Arpeggio frequency write (sub_11856).
+	 * Called from pollActiveChannel when _writeVolumePending (field_11) is set.
+	 * Computes a modified frequency from _note + _octaveTranspose + field_11 - 1
+	 * and writes it to the OPL registers, preserving the key-on bit.
+	 */
+	void writeArpeggio();
+
+	/**
+	 * Clears the key-on bit (bit 5) of the 0xB0+ch register, silencing the
+	 * note while preserving pitch.  Called during _keyOnDelay countdown.
+	 */
+	void updateOctave();
+
+	/**
+	 * Arms the frequency sweep counter from the sample definition, then - if
+	 * _freqSweepCounter is zero - immediately triggers the note by writing
+	 * volume and frequency registers and setting the key-on bit.
+	 */
+	void noteOn();
+
+	/**
+	 * Writes all OPL operator registers for the patch pointed to by _samplePtr,
+	 * using _currentOpBase as the operator base.  Covers ADSR, KSL/TL,
+	 * feedback/algorithm, AM/VIB flags, and waveform-select registers.
+	 */
+	void writeSampleRegs();
+
+	/**
+	 * Loads all OPL registers for the patch assigned to the active channel,
+	 * covering both modulator and carrier operators, and sets up the channel's
+	 * sweep/frequency state from the AdlibSample definition.
+	 */
+	void loadSample();
+
+	/**
+	 * Main per-frame update: increments frame counters, polls all channels,
+	 * fires the tick callback, calls updateAllChannels, then runs the per-channel
+	 * frequency-sweep tick for all 9 voices.
+	 */
+	void update();
+
+	/**
+	 * Per-channel frequency-sweep tick for channel 'channelIndex' (0-based).
+	 * When _freqSweepCounter > 0: adds _freqStep to _freqAccum, decrements the
+	 * counter, and - when it reaches zero - zeroes the voice's frequency registers.
+	 * Sets _anySweepActive when the counter is still non-zero.
+	 */
+	void update1(int channelIndex);
+
+	/**
+	 * Writes a frequency value directly to the OPL registers for 'voice'.
+	 * The high byte selects the block/octave; the low byte is the F-number LSB.
+	 */
+	void setFrequency(uint8 voice, uint16 freq);
+
+	/**
+	 * Noise-channel inner tick: picks a random frequency offset masked by
+	 * _noiseFreqMask, adds _freqAccum, and writes the result to the voice
+	 * frequency registers.
+	 */
+	void noise_inner(int channelIndex);
+
+	/**
+	 * Calls processChannelFade for all 9 channels each frame.
+	 */
+	void updateAllChannels();
+
+	/**
+	 * Advances pSrc by one, reads two bytes little-endian, and returns the word.
+	 * Used by the bytecode opcodes that take 16-bit operands.
+	 */
+	uint16 readWord_impl();
+
+	/**
+	 * Deferred-callback tick: decrements _callbackCounter; when it reaches zero,
+	 * reloads it from _callbackPeriod and calls _callbackFnPtr (if non-null),
+	 * then clears _callbackFnPtr so it fires exactly once.
+	 */
+	void tickCallback();
+
+protected:
+	// ---- nine AdlibChannel instances ------------------------------------
+	AdlibChannel _channel0, _channel1, _channel2;
+	AdlibChannel _channel3, _channel4, _channel5;
+	AdlibChannel _channel6, _channel7, _channel8;
+	AdlibChannel *_channels[ADLIB_CHANNEL_COUNT] = {
+		&_channel0, &_channel1, &_channel2,
+		&_channel3, &_channel4, &_channel5,
+		&_channel6, &_channel7, &_channel8
+	};
+
+	uint8  _adlibPorts[0x100] = { 0 };
+	Common::Array<AdlibSample> _samples;
+
+protected:
+	// =========================================================================
+	// Protected helpers  (used by ASound1–ASound9)
+	// =========================================================================
+
+	/**
+	 * Checks whether any of channels 0-6 (or 0-8 when _musicOnlyFlag is clear)
+	 * have a non-zero _activeCount.  Returns non-zero if sound is playing.
+	 * This is 'sub_1061A' in the disassembly.
+	 */
+	int isMusicChannelsActive();
+
+	/**
+	 * Like isMusicChannelsActive but scans all 9 channels unconditionally
+	 * (clears the ch0-6-only flag first).  This is 'sub_1064E'.
+	 */
+	int isAnyChannelActive();
+
+	/**
+	 * Schedule fn as the next deferred-load callback.
+	 * Does NOT touch _callbackCounter or _callbackPeriod — those are preserved
+	 * from the previous loader so the callback fires on the right beat.
+	 * Cast the derived-class member-function pointer with reinterpret_cast.
+	 */
+	void scheduleCallback(CallbackFunction fn) { _callbackFnPtr = fn; }
+
+	/**
+	 * Arm the periodic timer and clear any pending callback pointer.
+	 * Call at the head of every immediate-load function (symmetric counter/period).
+	 */
+	void resetCallbackTimer(uint16 period) {
+		_callbackFnPtr = nullptr;
+		_callbackCounter = period;
+		_callbackPeriod = period;
+	}
+
+	/**
+	 * Arm the periodic timer with separate counter and period values.
+	 * Used by command44 which sets counter=0x60 but period=0xE0.
+	 */
+	void resetCallbackTimerEx(uint16 counter, uint16 period) {
+		_callbackFnPtr = nullptr;
+		_callbackCounter = counter;
+		_callbackPeriod = period;
+	}
+
+	/** Set the music-piece index (word_12370) read by command18. */
+	void setMusicIndex(uint16 idx) { _musicIndex = idx; }
+	/** Read the current music-piece index. */
+	uint16 getMusicIndex() const { return _musicIndex; }
+
+	/** Write one script-variable register (byte_16A10[idx]). */
+	void setScriptVar(int idx, uint8 val) { _scriptVars[idx] = val; }
+	/**
+	 * Writes (reg, value) to the OPL chip and updates the _adlibPorts shadow
+	 * array so subsequent reads return the last-written value.
+	 */
+	void write(uint8 reg, uint8 value);
+
+	/** Updates and returns _randomSeed using a simple linear-feedback shift. */
+	uint16 getRandomNumber();
+
+	/**
+	 * Restores one OPL operator by re-writing the value already stored in the
+	 * _adlibPorts shadow array.  command7 uses this to resume playback after
+	 * a command6 pause without recalculating any TL values.
+	 * 'portIndex' is the operator register index into _adlibPorts.
+	 */
+	void adlib_channelOn(uint8 portIndex);
+
+	/** Sets _pollResult and _resultFlag to indicate that sound is playing. */
+	void signalSoundPlaying();
+
+	/**
+	 * Iterates over all 9 channels, sets _activeChannelPtr / _activeChannelNumber,
+	 * and calls pollActiveChannel for each one.
+	 */
+	void pollAllChannels();
+
+	/**
+	 * Per-channel bytecode interpreter, called once per frame per active channel.
+	 *
+	 * Sound data is a sequence of (note, duration) byte pairs plus command bytes
+	 * with the high bit set (0x80-0xFF).  The upper nibble of a command byte
+	 * (after masking out the high bit) selects one of seven opcode groups:
+	 *
+	 *   0x0_  -> opcodes1  (patch/velocity/volume/vibrato/transpose/arpeggio)
+	 *   0x1_  -> opcodes2  (inner/outer loop control, restart, branch/call)
+	 *   0x2_  -> opcodes3  (tempo, script-variable arithmetic, call-by-address)
+	 *   0x3_  -> opcodes4  (script-variable load/store/copy/inc/dec)
+	 *   0x4_  -> opcodes5  (script-variable ALU: add/sub/mul/div with imm or var)
+	 *   0x5_  -> opcodes6  (extended: random-range, indexed table read/write)
+	 *   0x6_  -> opcodes7  (driver-level calls: command dispatch, etc.)
+	 *
+	 * The lower nibble is passed as the sub-opcode to each group handler.
+	 * Note bytes (high bit clear) consume one duration tick per call.
+	 */
+	void pollActiveChannel();
+
+	/** Returns true if the sound data block at 'ptr' is already playing. */
+	bool isSoundActive(byte *ptr) const;
+
+	/**
+	 * Scans channels 0-6 for an empty slot (_activeCount == 0) and calls
+	 * load() on the first one found.  Falls through to findFreeChannelFull
+	 * for channels 6-8 when _findChannelMode != 1.
+	 */
+	void findFreeChannel(byte *soundData);
+
+	/**
+	 * Extends the search to channels 7-8, then checks for pending-stop
+	 * channels (which can be pre-empted), working in reverse priority order
+	 * (ch8, ch7, ch6 ... ch0).
+	 */
+	void findFreeChannelFull(byte *soundData);
+
+	/** Returns a pointer to the sound data at the given offset. */
+	byte *loadData(int offset) {
+		return &_soundData[offset];
+	}
+
+protected:
+	// =========================================================================
+	// Core commands (0-8)  -  identical in purpose to Return of the Phantom
+	// =========================================================================
+
+	/**
+	 * command0: Full hardware reset.
+	 *   1. Reset all 9 channels.
+	 *   2. Mute all operator TL registers (0x40-0x55) to 0x3F.
+	 *   3. Zero remaining operator registers (0x60-0xFF and 0x01-0x3F).
+	 *   4. Write Waveform Select Enable (register 0x01 = 0x20).
+	 *   5. Reset the tick callback.
+	 */
+	int command0();
+
+	/**
+	 * command1: Fade out all channels.
+	 *   Calls command3 (fade music channels 0-6) then command5 (fade SFX 7-8).
+	 */
+	int command1();
+
+	/**
+	 * command2: Fade out music channels 0-6.
+	 *   Calls AdlibChannel::setPtr2 on each, redirecting to the null stream
+	 *   and arming a one-step fade.
+	 */
+	int command2();
+
+	/**
+	 * command3: Fade out music channels 0-6 with pending-stop.
+	 *   Calls AdlibChannel::enable on channels 0-6.
+	 */
+	int command3();
+
+	/**
+	 * command4: Fade out SFX channels 7-8.
+	 *   Calls AdlibChannel::setPtr2 on channels 7 and 8.
+	 */
+	int command4();
+
+	/**
+	 * command5: Stop SFX channels 7-8 with pending-stop flag.
+	 *   Calls AdlibChannel::enable on channels 7 and 8, letting each channel
+	 *   finish its current OPL envelope before going idle.
+	 */
+	int command5();
+
+	/**
+	 * command6: Pause playback.
+	 *   Saves each channel's _freqSweepCounter into _savedSweepCounter, zeroes
+	 *   _freqSweepCounter on all channels, then mutes all 22 operator TL
+	 *   registers (the byte_1239B table covers all operator slots 0x40-0x55).
+	 *   Sets _isDisabled to prevent further updates.
+	 */
+	int command6();
+
+	/**
+	 * command7: Resume playback.
+	 *   Restores operator volumes for all channels from _adlibPorts shadow,
+	 *   copies _savedSweepCounter back to _freqSweepCounter on all channels,
+	 *   signals sound playing if any channel was active, then clears _isDisabled.
+	 */
+	int command7();
+
+	/**
+	 * command8: Returns non-zero if any of the 9 channels has a non-zero
+	 *   _activeCount (i.e. sound is currently playing).
+	 *   Also clears the music-only flag (byte_12393 = 0) so the check covers
+	 *   all 9 channels.
+	 */
+	int command8();
+
+	/**
+	 * Calls a function at a fixed offset within the sound driver.
+	 * @param offset		Offset of the function
+	 */
+	virtual void callFunction(uint16 offset);
+
+	// =========================================================================
+	// Music-index launcher (called via command18)
+	// =========================================================================
+
+	/**
+	 * command18: Re-entrant music launcher.
+	 *   First calls command1 to fade current output, then branches on _musicIndex
+	 *   (word_12370):
+	 *     <= 0x12  -> calls off_11A26 table (commands 16-19)
+	 *     > 0x12  -> calls funcs_12251 table (commands 32-49), index = _musicIndex - 0x20
+	 */
+	int command18();
+
+public:
+	/**
+	 * Constructor.
+	 * @param mixer       Mixer instance
+	 * @param opl         OPL chip instance
+	 * @param filename    Path to the .DR1 (or equivalent) sound-driver file
+	 * @param dataOffset  Offset in the file of the data segment
+	 * @param dataSize    Size of the data segment
+	 */
+	ASound(Audio::Mixer *mixer, OPL::OPL *opl, const Common::Path &filename,
+		int dataOffset, int dataSize);
+
+	~ASound() override {
+	}
+
+	/** Stop all currently playing sounds (wraps command0). */
+	virtual int stop() override;
+
+	/** Main poll method; drives the per-frame update. */
+	int poll() override;
+
+	/**
+	 * Noise channel tick: for each of the 9 channels calls noise_inner,
+	 * which randomises the voice frequency each frame using _noiseFreqMask.
+	 */
+	void noise() override;
+
+	/**
+	 * Starts playback of the sound data at the given byte offset within the
+	 * driver's data segment, using findFreeChannelFull to select a channel.
+	 */
+	void playSound(int offset);
+
+	void setVolume(int volume) override {
+		// TODO: implement if needed
+	}
+};
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/forest.cpp b/engines/mads/madsv2/forest/forest.cpp
index af0c9cc5913..4f2a10e778c 100644
--- a/engines/mads/madsv2/forest/forest.cpp
+++ b/engines/mads/madsv2/forest/forest.cpp
@@ -21,11 +21,34 @@
 
 #include "engines/util.h"
 #include "mads/madsv2/console.h"
-#include "mads/madsv2/forest/forest.h"
+#include "mads/madsv2/core/attr.h"
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/env.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/object.h"
+#include "mads/madsv2/core/pal.h"
+#include "mads/madsv2/core/rail.h"
 #include "mads/madsv2/core/screen.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/forest.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/main.h"
+#include "mads/madsv2/forest/sound_forest.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/section2.h"
+#include "mads/madsv2/forest/rooms/section3.h"
+#include "mads/madsv2/forest/rooms/section4.h"
+#include "mads/madsv2/forest/rooms/section5.h"
+#include "mads/madsv2/forest/rooms/section9.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/core/mps_installer.h"
 
 namespace MADS {
 namespace MADSV2 {
@@ -39,135 +62,74 @@ Common::Error ForestEngine::run() {
 	// Create a debugger console
 	setDebugger(new Console());
 
+	// Set up to read mpslabs installer archive if needed
+	if (_gameDescription->desc.flags & GF_INSTALLER) {
+		Common::Archive *arch = MpsInstaller::open("MPSLABS");
+		if (arch)
+			SearchMan.add("mpslabs", arch);
+	}
+
 	// Set up sound manager
-	_soundManager = nullptr; // new ForestSoundManager(_mixer, _soundFlag);
-	//_soundManager->validate();
+	_soundManager = new ForestSoundManager(_mixer, _soundFlag);
+	_soundManager->validate();
 
 	// Run the game
-	// TODO
+	Forest::forest_main();
 
 	return Common::kNoError;
 }
 
 void ForestEngine::global_init_code() {
-
-}
-
-void ForestEngine::section_music(int section_num) {
-
-}
-
-void ForestEngine::global_object_sprite() {
-
-}
-
-void ForestEngine::stop_walker_basic() {
-	int random;
 	int count;
-	int how_many;
-
-	random = imath_random(1, 30000);
-
-	switch (player.facing) {
-	case FACING_SOUTH:
-		if (random < 500) {
-			how_many = imath_random(4, 10);
-			for (count = 0; count < how_many; count++) {
-				player_add_stop_walker((random < 250) ? 1 : 2, 0);
-			}
-		} else if (random < 750) {
-			for (count = 0; count < 4; count++) {
-				player_add_stop_walker(1, 0);
-			}
-
-			player_add_stop_walker(0, 0);
-
-			for (count = 0; count < 4; count++) {
-				player_add_stop_walker(2, 0);
-			}
-
-			player_add_stop_walker(0, 0);
-		}
-		break;
-
-	case FACING_SOUTHEAST:
-	case FACING_SOUTHWEST:
-	case FACING_NORTHEAST:
-	case FACING_NORTHWEST:
-		if (random < 150) {
-			player_add_stop_walker(-1, 0);
-			player_add_stop_walker(1, 0);
-			for (count = 0; count < 6; count++) {
-				player_add_stop_walker(0, 0);
-			}
-		}
-		break;
-
-	case FACING_EAST:
-	case FACING_WEST:
-		if (random < 250) {
-			player_add_stop_walker(-1, 0);
-			how_many = imath_random(2, 6);
-			for (count = 0; count < how_many; count++) {
-				player_add_stop_walker(2, 0);
-			}
-			player_add_stop_walker(1, 0);
-			player_add_stop_walker(0, 0);
-			player_add_stop_walker(0, 0);
-		} else if (random < 500) {
-			WRITE_LE_UINT32(&global[walker_timing], kernel.clock);
-		}
-		break;
-
-	case FACING_NORTH:
-		if (random < 250) {
-			player_add_stop_walker(-1, 0);
-			how_many = imath_random(3, 7);
-			for (count = 0; count < how_many; count++) {
-				player_add_stop_walker(2, 0);
-			}
-			player_add_stop_walker(1, 0);
-			player_add_stop_walker(0, 0);
-		}
-		break;
 
+	for (count = 0; count < GLOBAL_LIST_SIZE; count++) {
+		global[count] = 0;
 	}
-}
 
-void ForestEngine::stop_walker_tricks() {
+	player.facing = FACING_NORTH;
+	player.turn_to_facing = FACING_NORTH;
+}
 
+void ForestEngine::section_music(int section_num) {
+	switch (section_num) {
+	case 1: Rooms::section_1_music(); break;
+	case 2: Rooms::section_2_music(); break;
+	case 3: Rooms::section_3_music(); break;
+	case 4: Rooms::section_4_music(); break;
+	case 5: Rooms::section_5_music(); break;
+	case 9: Rooms::section_9_music(); break;
+	}
 }
 
 void ForestEngine::global_section_constructor() {
-
+	Forest::global_section_constructor();
 }
 
 void ForestEngine::syncRoom(Common::Serializer &s) {
-
+	Forest::sync_room(s);
 }
 
 void ForestEngine::global_daemon_code() {
-
 }
 
 void ForestEngine::global_pre_parser_code() {
-
 }
 
 void ForestEngine::global_parser_code() {
+}
 
+void ForestEngine::global_object_examine() {
 }
 
 void ForestEngine::global_error_code() {
-
 }
 
 void ForestEngine::global_room_init() {
-
 }
 
 void ForestEngine::global_sound_driver() {
-
+	Common::strcpy_s(kernel.sound_driver, "/");
+	env_catint(kernel.sound_driver, new_section, 1);
 }
 
 } // namespace Forest
diff --git a/engines/mads/madsv2/forest/forest.h b/engines/mads/madsv2/forest/forest.h
index 577492d4ef8..e7f9d2f6662 100644
--- a/engines/mads/madsv2/forest/forest.h
+++ b/engines/mads/madsv2/forest/forest.h
@@ -19,8 +19,8 @@
  *
  */
 
-#ifndef MADS_FOREST_FOREST_H
-#define MADS_FOREST_FOREST_H
+#ifndef MADS_FOREST_H
+#define MADS_FOREST_H
 
 #include "mads/madsv2/engine.h"
 
@@ -30,9 +30,7 @@ namespace Forest {
 
 class ForestEngine : public MADSV2Engine {
 private:
-	static void global_object_sprite();
-	static void stop_walker_basic();
-	static void stop_walker_tricks();
+	static void global_object_examine();
 
 public:
 	ForestEngine(OSystem *syst, const MADSGameDescription *gameDesc) :
diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
new file mode 100644
index 00000000000..3d926fd63b1
--- /dev/null
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -0,0 +1,166 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/error.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+namespace Rooms {
+
+// Section preloads
+extern void section_1_preload();
+extern void section_2_preload();
+extern void section_3_preload();
+extern void section_4_preload();
+extern void section_5_preload();
+extern void section_9_preload();
+
+extern void room_101_synchronize(Common::Serializer &s);
+extern void room_101_synchronize(Common::Serializer &s);
+extern void room_103_synchronize(Common::Serializer &s);
+extern void room_104_synchronize(Common::Serializer &s);
+extern void room_106_synchronize(Common::Serializer &s);
+extern void room_107_synchronize(Common::Serializer &s);
+extern void room_199_synchronize(Common::Serializer &s);
+extern void room_201_synchronize(Common::Serializer &s);
+extern void room_203_synchronize(Common::Serializer &s);
+extern void room_204_synchronize(Common::Serializer &s);
+extern void room_205_synchronize(Common::Serializer &s);
+extern void room_210_synchronize(Common::Serializer &s);
+extern void room_211_synchronize(Common::Serializer &s);
+extern void room_220_synchronize(Common::Serializer &s);
+extern void room_221_synchronize(Common::Serializer &s);
+extern void room_301_synchronize(Common::Serializer &s);
+extern void room_302_synchronize(Common::Serializer &s);
+extern void room_303_synchronize(Common::Serializer &s);
+extern void room_304_synchronize(Common::Serializer &s);
+extern void room_305_synchronize(Common::Serializer &s);
+extern void room_306_synchronize(Common::Serializer &s);
+extern void room_307_synchronize(Common::Serializer &s);
+extern void room_308_synchronize(Common::Serializer &s);
+extern void room_321_synchronize(Common::Serializer &s);
+extern void room_322_synchronize(Common::Serializer &s);
+extern void room_401_synchronize(Common::Serializer &s);
+extern void room_402_synchronize(Common::Serializer &s);
+extern void room_403_synchronize(Common::Serializer &s);
+extern void room_404_synchronize(Common::Serializer &s);
+extern void room_405_synchronize(Common::Serializer &s);
+extern void room_420_synchronize(Common::Serializer &s);
+extern void room_501_synchronize(Common::Serializer &s);
+extern void room_503_synchronize(Common::Serializer &s);
+extern void room_509_synchronize(Common::Serializer &s);
+extern void room_510_synchronize(Common::Serializer &s);
+extern void room_520_synchronize(Common::Serializer &s);
+extern void room_901_synchronize(Common::Serializer &s);
+extern void room_903_synchronize(Common::Serializer &s);
+extern void room_904_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+
+void global_section_constructor() {
+	section_preload_code_pointer = NULL;
+	section_room_constructor = NULL;
+	section_init_code_pointer = NULL;
+	section_parser_code_pointer = NULL;
+	section_daemon_code_pointer = NULL;
+
+	room_preload_code_pointer = NULL;
+	room_init_code_pointer = NULL;
+	room_daemon_code_pointer = NULL;
+	room_pre_parser_code_pointer = NULL;
+	room_parser_code_pointer = NULL;
+	room_error_code_pointer = NULL;
+	room_shutdown_code_pointer = NULL;
+
+	switch (new_section) {
+	case 1:
+		section_preload_code_pointer = Rooms::section_1_preload;
+		break;
+	case 2:
+		section_preload_code_pointer = Rooms::section_2_preload;
+		break;
+	case 3:
+		section_preload_code_pointer = Rooms::section_3_preload;
+		break;
+	case 4:
+		section_preload_code_pointer = Rooms::section_4_preload;
+		break;
+	case 5:
+		section_preload_code_pointer = Rooms::section_5_preload;
+		break;
+	case 9:
+		section_preload_code_pointer = Rooms::section_9_preload;
+		break;
+	}
+}
+
+void sync_room(Common::Serializer &s) {
+	switch (new_room) {
+	case 101: Rooms::room_101_synchronize(s); break;
+	case 103: Rooms::room_103_synchronize(s); break;
+	case 104: Rooms::room_104_synchronize(s); break;
+	case 106: Rooms::room_106_synchronize(s); break;
+	case 107: Rooms::room_107_synchronize(s); break;
+	case 199: Rooms::room_199_synchronize(s); break;
+	case 201: Rooms::room_201_synchronize(s); break;
+	case 203: Rooms::room_203_synchronize(s); break;
+	case 204: Rooms::room_204_synchronize(s); break;
+	case 205: Rooms::room_205_synchronize(s); break;
+	case 210: Rooms::room_210_synchronize(s); break;
+	case 211: Rooms::room_211_synchronize(s); break;
+	case 220: Rooms::room_220_synchronize(s); break;
+	case 221: Rooms::room_221_synchronize(s); break;
+	case 301: Rooms::room_301_synchronize(s); break;
+	case 302: Rooms::room_302_synchronize(s); break;
+	case 303: Rooms::room_303_synchronize(s); break;
+	case 304: Rooms::room_304_synchronize(s); break;
+	case 305: Rooms::room_305_synchronize(s); break;
+	case 306: Rooms::room_306_synchronize(s); break;
+	case 307: Rooms::room_307_synchronize(s); break;
+	case 308: Rooms::room_308_synchronize(s); break;
+	case 321: Rooms::room_321_synchronize(s); break;
+	case 322: Rooms::room_322_synchronize(s); break;
+	case 401: Rooms::room_401_synchronize(s); break;
+	case 402: Rooms::room_402_synchronize(s); break;
+	case 403: Rooms::room_403_synchronize(s); break;
+	case 404: Rooms::room_404_synchronize(s); break;
+	case 405: Rooms::room_405_synchronize(s); break;
+	case 420: Rooms::room_420_synchronize(s); break;
+	case 501: Rooms::room_501_synchronize(s); break;
+	case 503: Rooms::room_503_synchronize(s); break;
+	case 509: Rooms::room_509_synchronize(s); break;
+	case 510: Rooms::room_510_synchronize(s); break;
+	case 520: Rooms::room_520_synchronize(s); break;
+	case 901: Rooms::room_901_synchronize(s); break;
+	case 903: Rooms::room_903_synchronize(s); break;
+	case 904: Rooms::room_904_synchronize(s); break;
+	default: break;
+	}
+}
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
new file mode 100644
index 00000000000..b64a68b9aca
--- /dev/null
+++ b/engines/mads/madsv2/forest/global.h
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_GLOBAL_H
+#define MADS_FOREST_GLOBAL_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/vocabh.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+extern void global_section_constructor();
+extern void sync_room(Common::Serializer &s);
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/mads/inventory.h b/engines/mads/madsv2/forest/mads/inventory.h
new file mode 100644
index 00000000000..daf83353cf1
--- /dev/null
+++ b/engines/mads/madsv2/forest/mads/inventory.h
@@ -0,0 +1,37 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_MADS_INVENTORY_H
+#define MADS_FOREST_MADS_INVENTORY_H
+
+#include "common/scummsys.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+//enum {};
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/mads/mads.cpp b/engines/mads/madsv2/forest/mads/mads.cpp
new file mode 100644
index 00000000000..d058cc2d316
--- /dev/null
+++ b/engines/mads/madsv2/forest/mads/mads.cpp
@@ -0,0 +1,25 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/quotes.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
diff --git a/engines/mads/madsv2/forest/mads/quotes.h b/engines/mads/madsv2/forest/mads/quotes.h
new file mode 100644
index 00000000000..65cadc2ddbf
--- /dev/null
+++ b/engines/mads/madsv2/forest/mads/quotes.h
@@ -0,0 +1,37 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_MADS_QUOTES_H
+#define MADS_FOREST_MADS_QUOTES_H
+
+#include "common/scummsys.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+//enum {};
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/mads/sounds.h b/engines/mads/madsv2/forest/mads/sounds.h
new file mode 100644
index 00000000000..84513db6c9d
--- /dev/null
+++ b/engines/mads/madsv2/forest/mads/sounds.h
@@ -0,0 +1,37 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_MADS_SOUNDS_H
+#define MADS_FOREST_MADS_SOUNDS_H
+
+#include "common/scummsys.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+//enum {};
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/mads/words.h b/engines/mads/madsv2/forest/mads/words.h
new file mode 100644
index 00000000000..8ba2af5b3ac
--- /dev/null
+++ b/engines/mads/madsv2/forest/mads/words.h
@@ -0,0 +1,37 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_MADS_WORDS_H
+#define MADS_FOREST_MADS_WORDS_H
+
+#include "common/scummsys.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+//enum {};
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/main.cpp b/engines/mads/madsv2/forest/main.cpp
new file mode 100644
index 00000000000..eac24e274b3
--- /dev/null
+++ b/engines/mads/madsv2/forest/main.cpp
@@ -0,0 +1,278 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "common/config-manager.h"
+#include "gui/saveload.h"
+#include "mads/madsv2/forest/main.h"
+#include "mads/madsv2/animview/animview.h"
+#include "mads/madsv2/textview/textview.h"
+#include "mads/madsv2/core/env.h"
+#include "mads/madsv2/core/error.h"
+#include "mads/madsv2/core/fileio.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/himem.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/magic.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/mcga.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/core/pal.h"
+#include "mads/madsv2/core/player.h"
+#include "mads/madsv2/core/quote.h"
+#include "mads/madsv2/core/speech.h"
+#include "mads/madsv2/forest/main_menu.h"
+#include "mads/madsv2/forest/menus.h"
+#include "mads/madsv2/engine.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+constexpr bool SHOW_LINES = true;
+constexpr byte LINE_COLOR = 2;
+
+char *quotes;
+
+static void main_menu_main() {
+	auto &screen = *g_engine->getScreen();
+	Palette palette;
+
+	if (!kernel_game_startup(19, KERNEL_STARTUP_CURSOR | KERNEL_STARTUP_INTERRUPT | KERNEL_STARTUP_FONT,
+		nullptr, nullptr)) {
+		matte_init(0xFFFF);
+		bool valid = !kernel_room_startup(920, 0, nullptr, true, true);
+
+		// Setup scr_work to use the full surface of scr_main (i.e. 320x200).
+		// Because of this, viewing_at_y is actually == 0
+		scr_work.y = picture_map.viewport_y;
+		viewing_at_y = (200 - scr_work.y) >> 1;
+
+		mouse_cursor_sprite(cursor, 7);
+		mouse_show();
+		mouse_force(280, 126);
+		mouse_hide();
+
+		mouse_cursor_sprite(cursor, 1);
+		kernel_seq_init();
+		kernel_message_init();
+		kernel_animation_init();
+		kernel_init_dynamic();
+
+		picture_view_x = 0;
+		picture_view_y = 0;
+		new_background = true;
+
+		if (valid) {
+			memset(&palette, 0, sizeof(palette));
+			mcga_setpal(&palette);
+			mouse_cursor_sprite(cursor, 1);
+
+			if (SHOW_LINES && viewing_at_y != 0) {
+				screen.hLine(0, viewing_at_y - 2, 319, LINE_COLOR);
+				screen.hLine(0, scr_work.y + viewing_at_y + 1, 319, LINE_COLOR);
+			}
+
+			kernel_load_sound_driver("*#SOUND.DR9", 'N', 544, 0, 49);
+
+			menu_control();
+
+			if (selected_item >= 0) {
+				// Zero out the first 3 entries of both magic color arrays
+				for (int i = 0; i < 3; i++) {
+					magic_color_values[i] = 0;
+					magic_color_flags[i] = 0;
+				}
+
+				mcga_getpal(&palette);
+
+				magic_fade_to_grey(palette, 0, 0x10, 1, 1, 0, 0, 0);
+			}
+		}
+
+		free(quotes);
+		kernel_unload_sound_driver();
+		kernel_game_shutdown();
+	}
+
+	mcga_reset();
+}
+
+static void main_cold_data_init() {
+	debugger_reset = game_debugger_reset;
+	debugger_update = game_debugger;
+	game_menu_routine = global_game_menu;
+	game_menu_init = global_menu_system_init;
+	game_menu_exit = global_menu_system_shutdown;
+	game_emergency_save = global_emergency_save;
+
+	Common::strcpy_s(config_file_name, "config.dra");
+	Common::strcpy_s(save_game_key, "drag");
+	Common::strcpy_s(restart_game_key, "dragon");
+
+	Common::strcpy_s(player.series_name, "RAL");
+	player.walker_must_reload = true;
+	player.walker_loads_first = false;
+	player.walker_visible = true;
+	player.scaling_velocity = true;
+
+	Common::strcpy_s(kernel_cheating_password, "WIDECHEW");
+	kernel_cheating_allowed = strlen(kernel_cheating_password);
+
+	kernel.cheating = gDebugLevel == 9 ? kernel_cheating_allowed : 0;
+}
+
+static void game_main(int argc, const char **argv) {
+	int count;
+	int mads_mode;
+	const char *scan;
+
+	pack_enable_pfab_explode();
+
+	mads_mode = env_verify();
+
+	new_section = 1;
+	new_room = 101;
+	player.x = 160;
+	player.y = 78;
+
+	player.target_facing = FACING_NORTH;
+
+	game_cold_data_init();
+	main_cold_data_init();
+	g_engine->readConfigFile();
+	global_load_config_parameters();
+
+	if (argc >= 2) {
+		for (count = 1; count < argc; count++) {
+			if (strchr("-/", argv[count][0]) != NULL) {
+				for (scan = argv[count] + 1; *scan != 0; scan++) {
+					flag_parse(&scan);
+				}
+			} else if (argv[count][0] == '?') {
+				show_logo();
+
+				if (!mads_mode) env_search_mode = ENV_SEARCH_CONCAT_FILES;
+				error_dump_file("*warn2.dat");
+				goto done;
+			}
+		}
+	}
+
+	if (report_version) {
+		show_version();
+		goto done;
+	}
+
+	if (fileio_exist("global.hag")) {
+		art_hags_are_on_hd = true;
+	} else {
+		art_hags_are_on_hd = false;
+	}
+
+	himem_startup();
+
+	himem_shutdown();
+
+	if (!mads_mode && (env_search_mode == ENV_SEARCH_MADS_PATH))
+		error("false start");
+
+	game_control();
+
+done:
+	global_unload_config_parameters();
+
+	if (fileio_exist("config.for")) {
+		global_write_config_file();
+	}
+	if (chain_flag && (win_status || force_chain) && (key_abort_level < 2)) {
+		warning("TODO: chain_execute");
+	} else {
+		if (win_status) {
+			debug("(Ending: %d)", win_status);
+		}
+	}
+}
+
+void forest_main() {
+	static const char *CMD_LINE[] = { nullptr, "-p" };
+
+	pack_enable_pfab_explode();
+	if (!env_verify())
+		env_search_mode = ENV_SEARCH_CONCAT_FILES;
+
+	if (ConfMan.getBool("start_game") || ConfMan.hasKey("save_slot"))
+		selected_item = 0;
+	else if (ConfMan.getBool("start_intro"))
+		selected_item = 3;
+	else
+		selected_item = -1;
+
+	while (!g_engine->shouldQuit()) {
+		g_engine->getScreen()->clear();
+
+		switch (selected_item) {
+		case -1:
+			main_menu_main();
+			break;
+
+		case 0:
+			game_main(2, CMD_LINE);
+			return;
+
+		case 1: {
+			// Resume savegame
+			// Get a list of saves and choose the last one
+			auto saves = g_engine->listSaves();
+			if (!saves.empty())
+				savegame_slot = saves.back().getSaveSlot();
+
+			// Start the game, which will also load the selected savegame
+			game_main(2, CMD_LINE);
+			return;
+		}
+
+		case 2:
+			// Restore savegame
+			game_restore_flag = 2;
+			game_main(2, CMD_LINE);
+			return;
+
+		case 3:
+			AnimView::animview_main("@forest");
+			selected_item = -1;
+			break;
+
+		case 4:
+			// Exit
+			return;
+
+		default:
+			// Credits
+			TextView::textview_main("credits");
+			selected_item = -1;
+			break;
+		}
+	}
+}
+
+} // namespace Foreste
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/main.h b/engines/mads/madsv2/forest/main.h
new file mode 100644
index 00000000000..7d74c096016
--- /dev/null
+++ b/engines/mads/madsv2/forest/main.h
@@ -0,0 +1,37 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_MAIN_H
+#define MADS_FOREST_MAIN_H
+
+#include "common/scummsys.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+extern void forest_main();
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/main_menu.cpp b/engines/mads/madsv2/forest/main_menu.cpp
new file mode 100644
index 00000000000..7dff7b0108e
--- /dev/null
+++ b/engines/mads/madsv2/forest/main_menu.cpp
@@ -0,0 +1,433 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/engine.h"
+#include "mads/madsv2/core/general.h"
+#include "mads/madsv2/core/config.h"
+#include "mads/madsv2/core/font.h"
+#include "mads/madsv2/core/speech.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/sprite.h"
+#include "mads/madsv2/core/error.h"
+#include "mads/madsv2/core/room.h"
+#include "mads/madsv2/core/fileio.h"
+#include "mads/madsv2/core/env.h"
+#include "mads/madsv2/core/buffer.h"
+#include "mads/madsv2/core/mem.h"
+#include "mads/madsv2/core/pal.h"
+#include "mads/madsv2/core/timer.h"
+#include "mads/madsv2/core/mcga.h"
+#include "mads/madsv2/core/cycle.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/core/screen.h"
+#include "mads/madsv2/core/video.h"
+#include "mads/madsv2/core/lib.h"
+#include "mads/madsv2/core/keys.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/btype.h"
+#include "mads/madsv2/core/hspot.h"
+#include "mads/madsv2/core/magic.h"
+#include "mads/madsv2/core/ems.h"
+#include "mads/madsv2/core/xms.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/pack.h"
+#include "mads/madsv2/core/quote.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/main_menu.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+#define CONFIG_FILE_NAME "CONFIG.PHA"
+
+#define EYE_QUOTES      16
+#define EYE_HOTSPOT     32
+#define EYE_MESSAGES    2
+
+#undef MESSAGE_COLOR
+#define MESSAGE_COLOR   1284
+
+
+ConfigFile config_file;
+
+int mads_mode = false;
+
+FontPtr font = NULL;
+int font_auto_spacing = -1;
+
+bool new_background   = false;
+int room_number       = 0;
+
+int win_status        = 0;
+
+bool force_opening    = false;
+bool dont_return      = false;
+bool white_bars       = true;
+byte white_border     = 2;
+
+char command_line[COMMAND_LINE_MAX][80];
+char *command_list[COMMAND_LINE_MAX+1];
+int  command_line_size = 0;
+
+int  use_mouse_cursor_fix = false;
+
+bool going;
+int  menu_mode;
+int  must_perform_matte;
+
+long frame_clock;
+long now_clock;
+long menu_clock;
+
+int menu_state;
+int menu_appear_state;
+
+int highest_y = -1;
+int next_x    = -1;
+
+char sound_file[80]    = "*#SOUND.PH9";
+char sound_letter      = 'N';
+
+int  sound_board_address = 0x220;              /* Default sound board address */
+int  sound_board_type    = sound_board_roland; /* Default sound board type    */
+
+int  swapping = false;                   /* Flag when swapping in new background */
+int  swap_line;                          /* Currently active swap line           */
+long swap_clock;                         /* Time for next swap */
+
+int  current_item;
+int  current_eye   = false;
+int  eye_latch     = false;
+int  selected_item = -1;
+
+#define LEFT_EYE   0
+#define RIGHT_EYE  1
+
+extern char *quotes;
+int  eye_message[2];
+int  eye_pokes   = 0;
+int  recent_eye  = 0;
+int  rebel_base  = 0;
+int  poke_count_message = -1;
+int  poke_count         = 0;
+int  poke_counting      = false;
+
+char poke_count_buf[2][20];
+
+char bonus_buf[80] = "";
+
+Palette special_pal;                     /* Palette for fadeout */
+
+
+MenuItem menu_item[NUM_MENU_ITEMS+1];    /* Menu item array */
+
+static void start_series() {
+	int error_flag = true;
+	int count;
+	int handle;
+	char temp_buf[80];
+
+	for (count = 0; count < NUM_MENU_ITEMS; count++) {
+		handle = -1;
+		Common::strcpy_s(temp_buf, "*MAIN0.SS");
+		temp_buf[5] = (char)('0' + count);
+
+		handle = matte_load_series(temp_buf, 0, 0);
+		if (handle < 0) goto done;
+
+		menu_item[count].handle = handle;
+		menu_item[count].active = count != 6;	// Quotes button was never implemented
+		menu_item[count].status = 0;
+	}
+
+	error_flag = false;
+
+done:
+	if (error_flag) {
+		Common::strcpy_s(error_string, temp_buf);
+		error_report(ERROR_SERIES_LOAD_FAILED, WARNING, MODULE_UNKNOWN, count, handle);
+	}
+}
+
+static void stop_series() {
+	int count;
+
+	for (count = NUM_MENU_ITEMS - 1; count >= 0; count--) {
+		matte_deallocate_series(menu_item[count].handle, true);
+	}
+}
+
+static void start_hotspots() {
+	int count;
+	int x1, x2, y1, y2;
+	int xs, ys;
+	SeriesPtr series;
+
+	numspots = 0;
+
+	for (count = 0; count < NUM_MENU_ITEMS; count++) {
+		series = series_list[menu_item[count].handle];
+		xs = series->index[0].xs;
+		ys = series->index[0].ys;
+		x1 = series->index[0].x - (xs >> 1);
+		y1 = series->index[0].y - (ys - 1);
+
+		x2 = x1 + xs - 1;
+		y2 = y1 + ys - 1;
+		hspot_add(x1, y1, x2, y2, 1, count, mcga_mode);
+	}
+}
+
+static void process_menu() {
+	int myspot;
+
+	myspot = hspot_which(mouse_x, mouse_y - viewing_at_y, mcga_mode);
+
+	current_eye = false;
+
+	if ((myspot > 0) && mouse_any_stroke && (menu_mode == MENU_ACCEPTING_COMMANDS)) {
+		current_item = spot[myspot].num;
+		if (current_item >= EYE_HOTSPOT) {
+			current_item = -1;
+			current_eye = true;
+		}
+	} else {
+		current_item = -1;
+	}
+
+	if (mouse_stop_stroke && (current_item >= 0) && (menu_mode == MENU_ACCEPTING_COMMANDS)) {
+		selected_item = current_item;
+		menu_mode = MENU_DISAPPEARING;
+		menu_state = 1;
+	}
+}
+
+static void process_sprites() {
+	int count;
+	int sprite;
+	int series;
+	int look, match;
+	Image image;
+
+	for (count = 0; count < (int)image_marker; count++) {
+		if ((image_list[count].segment_id < KERNEL_SEGMENT_ANIMATION) ||
+			(image_list[count].segment_id > KERNEL_SEGMENT_ANIMATION_HIGH)) {
+			if (image_list[count].flags >= IMAGE_STATIC) {
+				image_list[count].flags = IMAGE_ERASE;
+			}
+		}
+	}
+
+	if (menu_mode == MENU_APPEARING) goto done;
+
+	for (count = 0; count < NUM_MENU_ITEMS; count++) {
+		if (menu_item[count].active) {
+			image.flags = IMAGE_UPDATE;
+			image.segment_id = (byte)(count + 1);
+
+			series = count;
+			if (menu_mode == MENU_ACCEPTING_COMMANDS) {
+				if (count != current_item) {
+					sprite = 1;
+				} else {
+					sprite = 2;
+				}
+			} else {
+				sprite = 1;
+			}
+
+			image.series_id = (byte)menu_item[series].handle;
+			image.sprite_id = sprite;
+
+			image.x = series_list[series]->index[sprite - 1].x;
+			image.y = series_list[series]->index[sprite - 1].y;
+
+			image.depth = 0;
+			image.scale = 100;
+
+			match = !(sprite <= MENU_HIGH_SPRITE);
+
+			for (look = 0; !match && (look < (int)image_marker); look++) {
+				if (image_list[look].segment_id == image.segment_id) {
+					if (memcmp(&image_list[look].series_id,
+						&image.series_id, 9) == 0) {
+						image_list[look].flags = IMAGE_STATIC;
+						match = true;
+					}
+				}
+			}
+
+			if (!match) {
+				image_list[image_marker] = image;
+				image_marker++;
+			}
+		}
+	}
+
+done:
+	;
+}
+
+void menu_control() {
+	int fx;
+	int mykey;
+	int last_frame = -1;
+	int now_frame;
+	int reset_frame;
+	int anim;
+	int initial_reset = false;
+
+	menu_mode = MENU_APPEARING;
+	menu_state = MENU_HIGH_SPRITE;
+	menu_appear_state = 0;
+	menu_clock = 999999999;
+
+	going = true;
+	must_perform_matte = false;
+
+	frame_clock = 0;
+
+	start_series();
+	start_hotspots();
+
+	anim = kernel_run_animation("*RM902A.AA", 0);
+
+	mouse_init_cycle();
+
+	while (going && !g_engine->shouldQuit()) {
+		if (keys_any()) {
+			mykey = keys_get();
+			switch (toupper(mykey)) {
+			case esc_key:
+				selected_item = 4;
+				going = false;
+				break;
+
+			case alt_x_key:
+			case alt_q_key:
+			case ctrl_x_key:
+			case ctrl_q_key:
+				going = false;
+				break;
+
+			case 'S':
+				menu_mode = MENU_APPEARING;
+				menu_state = MENU_HIGH_SPRITE;
+				menu_appear_state = 0;
+				break;
+
+			default:
+				if (menu_mode == MENU_APPEARING) {
+					menu_mode = MENU_ACCEPTING_COMMANDS;
+					kernel_reset_animation(anim, 105);
+					kernel_anim[anim].next_clock = kernel.clock;
+				}
+				break;
+			}
+		}
+
+		mouse_begin_cycle(false);
+
+		if (mouse_stroke_going) {
+			if (menu_mode == MENU_APPEARING) {
+				menu_mode = MENU_ACCEPTING_COMMANDS;
+				kernel_reset_animation(anim, 105);
+				kernel_anim[anim].next_clock = kernel.clock;
+				mouse_init_cycle();
+			}
+		}
+
+		if (menu_mode != MENU_APPEARING) {
+			mouse_show();
+		}
+
+		now_clock = timer_read();
+		kernel.clock = now_clock;
+
+		if (now_clock >= menu_clock) {
+			if (menu_mode == MENU_DISAPPEARING)
+				going = false;
+
+			menu_clock = now_clock + MENU_FRAME_RATE;
+		}
+
+		process_menu();
+		kernel_message_update_all();
+		kernel_process_all_animations();
+
+		reset_frame = -1;
+		now_frame = kernel_anim[anim].frame;
+		if (now_frame != last_frame) {
+			last_frame = now_frame;
+
+			if (now_frame < 105) {
+				if (menu_mode != MENU_APPEARING) {
+					if (!initial_reset) {
+						reset_frame = 105;
+						initial_reset = true;
+					}
+				}
+			}
+
+			if (now_frame >= 109) {
+				if (menu_mode == MENU_APPEARING)
+					menu_mode = MENU_ACCEPTING_COMMANDS;
+
+				reset_frame = 105;
+			}
+
+			if (reset_frame >= 0) {
+				kernel_reset_animation(anim, reset_frame);
+				last_frame = reset_frame;
+			}
+		}
+
+		if (now_clock >= frame_clock) {
+			process_sprites();
+
+			fx = new_background ? 1 : 0;
+			matte_frame(fx, false);
+
+			if (fx) {
+				now_clock = timer_read();
+				menu_clock = now_clock + MENU_FRAME_RATE;
+			}
+
+			must_perform_matte = false;
+			new_background = false;
+
+			frame_clock = now_clock + FRAME_RATE;
+		}
+
+		mouse_end_cycle(false, going);
+	}
+
+	kernel_abort_animation(anim);
+
+	stop_series();
+}
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/main_menu.h b/engines/mads/madsv2/forest/main_menu.h
new file mode 100644
index 00000000000..ae655e0f4c3
--- /dev/null
+++ b/engines/mads/madsv2/forest/main_menu.h
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_MAIN_MENU_H
+#define MADS_FOREST_MAIN_MENU_H
+
+#include "common/str.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+#define COMMAND_LINE_MAX        10
+
+#define FRAME_RATE              1
+#define MENU_FRAME_RATE         3
+
+#define NUM_MENU_ITEMS          6
+
+#define MENU_APPEARING          0
+#define MENU_ACCEPTING_COMMANDS 1
+#define MENU_DISAPPEARING       2
+
+#define MENU_HIGH_SPRITE        15
+
+typedef struct {
+	int handle;           /* Sprite series handle */
+	int active;           /* Menu item is active  */
+	int status;           /* Current status       */
+} MenuItem;
+
+extern bool new_background;
+extern int selected_item;
+
+extern void menu_control();
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/menus.cpp b/engines/mads/madsv2/forest/menus.cpp
new file mode 100644
index 00000000000..ee9fe1c8bb5
--- /dev/null
+++ b/engines/mads/madsv2/forest/menus.cpp
@@ -0,0 +1,615 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/config.h"
+#include "mads/madsv2/core/echo.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/global.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/keys.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/mem.h"
+#include "mads/madsv2/core/speech.h"
+#include "mads/madsv2/core/quote.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/menus.h"
+#include "mads/madsv2/forest/global.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+#define SAVE_MENU_PIXEL_WIDTH           200
+#define MAX_SAVES_ON_SCREEN             10
+
+#define SPACE_BETWEEN                   -1
+
+#define MAIN_MENU_ITEM_WIDTH            140
+#define MAIN_MENU_FORCE_WIDTH           160
+
+#define DIFFICULTY_MENU_FORCE_WIDTH     140
+
+#define OPTIONS_MENU_ITEM_WIDTH         160
+#define OPTIONS_MENU_FORCE_WIDTH        170
+#define OPTIONS_MENU_OFF_CENTER         10
+
+
+#define SAVE_SUCCESSFUL                 1
+#define RESTORE_SUCCESSFUL              2
+#define SAVE_FAILED                     3
+#define RESTORE_FAILED                  4
+
+
+int global_menu_direct_jump;
+int global_menu_force_restart;
+
+char *menu_quotes = NULL;
+
+
+static int global_save(int id, const char *save_game_name) {
+	int status;
+
+	game_save_name(id + 1);
+
+	if (g_engine->saveGameState(id + 1, save_game_name).getCode() == Common::kNoError)
+		status = SAVE_SUCCESSFUL;
+	else
+		status = SAVE_FAILED;
+
+	return status;
+}
+
+static int global_restore(int id) {
+	int status;
+
+	if (g_engine->loadGameState(id + 1).getCode() == Common::kNoError)
+		status = RESTORE_SUCCESSFUL;
+	else
+		status = RESTORE_FAILED;
+
+	WRITE_LE_UINT32(&global[walker_timing], 0);
+
+	return status;
+}
+
+void global_menu_system_init() {
+}
+
+void global_menu_system_shutdown() {
+	if (menu_quotes != NULL)
+		mem_free(menu_quotes);
+}
+
+static char *menu_quote(int id) {
+	return quote_string(menu_quotes, id);
+}
+
+void global_emergency_save() {
+	game_save_name(0);
+
+	if (scr_orig.data != NULL) mem_free(scr_orig.data);
+
+	g_engine->saveAutosaveIfEnabled();
+}
+
+static void global_alert(int status) {
+}
+
+static void global_menu_score() {
+}
+
+static void global_menu_save_restore(int save) {
+#if 0
+	int status = -1;
+	int selection;
+	char *save_game_name;
+	PopupItem *save_list;
+	PopupItem *go_button;
+	PopupItem *clear_button;
+	PopupItem *cancel_button;
+	PopupItem *result;
+
+	popup_dialog_create(game_menu_popup, GAME_DIALOG_HEAP, 20);
+
+	if (save) {
+		popup_message(menu_quote(quote_save_title), POPUP_CENTER, POPUP_FILL);
+	} else {
+		popup_message(menu_quote(quote_restore_title), POPUP_CENTER, POPUP_FILL);
+	}
+	popup_blank(4);
+
+	save_list = popup_savelist(game_save_directory, menu_quote(quote_menu_empty),
+		GAME_MAX_SAVE_SLOTS,
+		GAME_MAX_SAVE_LENGTH + 1,
+		GAME_MAX_SAVE_LENGTH,
+		SAVE_MENU_PIXEL_WIDTH,
+		MAX_SAVES_ON_SCREEN,
+		save, game.last_save);
+
+	if (save) {
+		go_button = popup_button(menu_quote(quote_menu_save), POPUP_BUTTON_LEFT);
+		clear_button = popup_button(menu_quote(quote_menu_clear), POPUP_CENTER);
+		game_menu_popup->clear_item = clear_button;
+	} else {
+		go_button = popup_button(menu_quote(quote_menu_restore), POPUP_BUTTON_LEFT);
+	}
+	cancel_button = popup_cancel_button(menu_quote(quote_menu_cancel));
+
+	result = popup_execute();
+
+	if (result == cancel_button) {
+		switch (game_menu_popup->key) {
+		case alt_x_key:
+		case ctrl_x_key:
+		case alt_q_key:
+		case ctrl_q_key:
+			game.going = false;
+			kernel.activate_menu = GAME_NO_MENU;
+			break;
+
+		case f1_key:
+			kernel.activate_menu = GAME_MAIN_MENU;
+			break;
+
+		case f2_key:
+			if (save) {
+				kernel.activate_menu = GAME_MAIN_MENU;
+			} else {
+				kernel.activate_menu = GAME_SAVE_MENU;
+			}
+			break;
+
+		case f3_key:
+			if (!save) {
+				kernel.activate_menu = GAME_MAIN_MENU;
+			} else {
+				kernel.activate_menu = GAME_RESTORE_MENU;
+			}
+			break;
+
+		case f4_key:
+			kernel.activate_menu = GAME_SCORE_MENU;
+			break;
+
+		case f5_key:
+			kernel.activate_menu = GAME_OPTIONS_MENU;
+			break;
+
+		default:
+			kernel.activate_menu = GAME_MAIN_MENU;
+			break;
+		}
+	} else {
+		kernel.activate_menu = GAME_NO_MENU;
+	}
+
+	popup_dialog_destroy();
+
+	if (result == go_button) {
+		selection = save_list->list->picked_element;
+		game.last_save = selection;
+		if (save) {
+			save_game_name = game_save_directory + ((GAME_MAX_SAVE_LENGTH + 1) * selection);
+			if (!strlen(save_game_name)) {
+				Common::strcpy_s(save_game_name, GAME_MAX_SAVE_LENGTH, menu_quote(quote_menu_unnamed));
+			}
+			status = global_save(selection, save_game_name);
+
+		} else {
+			status = global_restore(selection);
+		}
+
+		if (status > 0)
+			// Dummy name to signal save/load went ok
+			Common::strcpy_s(save_game_buf, "OK");
+	}
+
+	if (status >= 0) {
+		global_alert(status);
+	}
+#endif
+}
+
+static void global_menu_options() {
+#if 0
+	int initial_1, initial_2, initial_3;
+	int initial_4, initial_5, initial_6;
+	int former_music;
+	int former_sound;
+	PopupItem *music_item;
+	PopupItem *sound_item;
+	PopupItem *interface_item;
+	PopupItem *fade_item;
+	PopupItem *panning_item;
+	PopupItem *speech_item;
+	PopupItem *done_button;
+	PopupItem *cancel_button;
+	PopupItem *result;
+
+	global_unload_config_parameters();
+
+	popup_dialog_create(game_menu_popup, GAME_DIALOG_HEAP, 20);
+
+	popup_sprite(box_param.logo, 1, POPUP_CENTER, POPUP_FILL);
+	popup_blank(2);
+
+	former_music = config_file.music_flag;
+	former_sound = config_file.sound_flag;
+
+	initial_1 = config_file.music_flag ? 0 : 1;
+	initial_2 = config_file.sound_flag ? 0 : 1;
+	initial_6 = config_file.speech_flag ? 0 : 1;
+	initial_3 = config_file.interface_hotspots;
+	initial_4 = config_file.screen_fade;
+	initial_5 = config_file.panning_speed;
+
+	music_item = popup_menu(menu_quote(quote_options_item1),
+		POPUP_CENTER, POPUP_FILL, OPTIONS_MENU_ITEM_WIDTH,
+		OPTIONS_MENU_OFF_CENTER,
+		2, 40, initial_1);
+	popup_menu_option(music_item, menu_quote(quote_options_item1a));
+	popup_menu_option(music_item, menu_quote(quote_options_item1b));
+
+	popup_blank(SPACE_BETWEEN);
+
+
+	sound_item = popup_menu(menu_quote(quote_options_item2),
+		POPUP_CENTER, POPUP_FILL, OPTIONS_MENU_ITEM_WIDTH,
+		OPTIONS_MENU_OFF_CENTER,
+		2, 40, initial_2);
+	popup_menu_option(sound_item, menu_quote(quote_options_item2a));
+	popup_menu_option(sound_item, menu_quote(quote_options_item2b));
+
+	popup_blank(SPACE_BETWEEN);
+
+
+	speech_item = popup_menu(menu_quote(quote_options_item6),
+		POPUP_CENTER, POPUP_FILL, OPTIONS_MENU_ITEM_WIDTH,
+		OPTIONS_MENU_OFF_CENTER,
+		2, 40, initial_6);
+	popup_menu_option(speech_item, menu_quote(quote_options_item6a));
+	popup_menu_option(speech_item, menu_quote(quote_options_item6b));
+
+	popup_blank(SPACE_BETWEEN);
+
+
+	interface_item = popup_menu(menu_quote(quote_options_item3),
+		POPUP_CENTER, POPUP_FILL, OPTIONS_MENU_ITEM_WIDTH,
+		OPTIONS_MENU_OFF_CENTER,
+		2, 40, initial_3);
+	popup_menu_option(interface_item, menu_quote(quote_options_item3a));
+	popup_menu_option(interface_item, menu_quote(quote_options_item3b));
+
+	popup_blank(SPACE_BETWEEN);
+
+
+	fade_item = popup_menu(menu_quote(quote_options_item4),
+		POPUP_CENTER, POPUP_FILL, OPTIONS_MENU_ITEM_WIDTH,
+		OPTIONS_MENU_OFF_CENTER,
+		3, 40, initial_4);
+	popup_menu_option(fade_item, menu_quote(quote_options_item4a));
+	popup_menu_option(fade_item, menu_quote(quote_options_item4b));
+	popup_menu_option(fade_item, menu_quote(quote_options_item4c));
+
+	popup_blank(SPACE_BETWEEN);
+
+
+	panning_item = popup_menu(menu_quote(quote_options_item5),
+		POPUP_CENTER, POPUP_FILL, OPTIONS_MENU_ITEM_WIDTH,
+		OPTIONS_MENU_OFF_CENTER,
+		3, 40, initial_5);
+	popup_menu_option(panning_item, menu_quote(quote_options_item5a));
+	popup_menu_option(panning_item, menu_quote(quote_options_item5b));
+	popup_menu_option(panning_item, menu_quote(quote_options_item5c));
+
+
+
+	done_button = popup_button(menu_quote(quote_menu_done), POPUP_LEFT);
+	cancel_button = popup_cancel_button(menu_quote(quote_menu_cancel));
+
+	popup_width_force(OPTIONS_MENU_FORCE_WIDTH);
+
+	result = popup_execute();
+
+	kernel.activate_menu = GAME_MAIN_MENU;
+
+	if (result == cancel_button) {
+		switch (game_menu_popup->key) {
+		case alt_x_key:
+		case ctrl_x_key:
+		case alt_q_key:
+		case ctrl_q_key:
+			game.going = false;
+			kernel.activate_menu = GAME_NO_MENU;
+			break;
+
+		case f1_key:
+			kernel.activate_menu = GAME_MAIN_MENU;
+			break;
+
+		case f2_key:
+			kernel.activate_menu = GAME_SAVE_MENU;
+			break;
+
+		case f3_key:
+			kernel.activate_menu = GAME_RESTORE_MENU;
+			break;
+
+		case f4_key:
+			kernel.activate_menu = GAME_SCORE_MENU;
+			break;
+
+		case f5_key:
+			kernel.activate_menu = GAME_MAIN_MENU;
+			break;
+
+		default:
+			kernel.activate_menu = GAME_MAIN_MENU;
+			break;
+		}
+	}
+
+	if (result == done_button) {
+		config_file.music_flag = !music_item->list->picked_element;
+		config_file.sound_flag = !sound_item->list->picked_element;
+		config_file.speech_flag = !speech_item->list->picked_element;
+		config_file.interface_hotspots = interface_item->list->picked_element;
+		config_file.screen_fade = fade_item->list->picked_element;
+		config_file.panning_speed = panning_item->list->picked_element;
+
+		global_write_config_file();
+		global_load_config_parameters();
+
+		kernel.activate_menu = GAME_NO_MENU;
+	}
+
+	if ((former_music != config_file.music_flag) ||
+		(former_sound != config_file.sound_flag)) {
+		game_exec_function(section_music_reset_pointer);
+	}
+
+	if (!config_file.speech_flag) {
+		if (speech_system_active)
+			speech_all_off();
+	}
+
+	popup_dialog_destroy();
+#endif
+}
+
+static void global_menu_difficulty() {
+#if 0
+	PopupItem *easy_item;
+	PopupItem *result;
+
+	popup_dialog_create(game_menu_popup, GAME_DIALOG_HEAP, 20);
+
+	popup_message(menu_quote(quote_difficulty_title), POPUP_CENTER, POPUP_FILL);
+	popup_blank(6);
+
+	easy_item = popup_menu(menu_quote(quote_difficulty_item1),
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
+		0, 0, 0);
+
+	popup_blank(SPACE_BETWEEN);
+
+	(void)popup_menu(menu_quote(quote_difficulty_item2),
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
+		0, 0, 0);
+
+
+	popup_width_force(DIFFICULTY_MENU_FORCE_WIDTH);
+
+	game_menu_popup->cancel_item = NULL;
+
+	result = popup_execute();
+
+	kernel.activate_menu = GAME_NO_MENU;
+
+	if (result == easy_item) {
+		game.difficulty = EASY_MODE;
+	} else {
+		game.difficulty = HARD_MODE;
+	}
+
+	if (result == NULL) {
+		game.going = false;
+	}
+
+	switch (game_menu_popup->key) {
+	case alt_x_key:
+	case ctrl_x_key:
+	case alt_q_key:
+	case ctrl_q_key:
+	case esc_key:
+		game.going = false;
+		break;
+	}
+
+	popup_dialog_destroy();
+#endif
+}
+
+static void global_menu_main() {
+#if 0
+	PopupItem *save_item;
+	PopupItem *restore_item;
+	PopupItem *score_item;
+	PopupItem *options_item;
+	PopupItem *resume_item;
+	PopupItem *quit_item;
+	PopupItem *result;
+
+	popup_dialog_create(game_menu_popup, GAME_DIALOG_HEAP, 20);
+
+	popup_sprite(box_param.logo, 1, POPUP_CENTER, POPUP_FILL);
+	popup_blank(6);
+
+	save_item = popup_menu(menu_quote(quote_main_item1),
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
+		0, 0, 0);
+
+	popup_blank(SPACE_BETWEEN);
+
+	restore_item = popup_menu(menu_quote(quote_main_item2),
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
+		0, 0, 0);
+
+	popup_blank(SPACE_BETWEEN);
+
+	score_item = popup_menu(menu_quote(quote_main_item6),
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
+		0, 0, 0);
+
+	popup_blank(SPACE_BETWEEN);
+
+	options_item = popup_menu(menu_quote(quote_main_item3),
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
+		0, 0, 0);
+
+	popup_blank(SPACE_BETWEEN);
+
+	resume_item = popup_menu(menu_quote(quote_main_item4),
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
+		0, 0, 0);
+
+	popup_blank(SPACE_BETWEEN);
+
+	quit_item = popup_menu(menu_quote(quote_main_item5),
+		POPUP_CENTER, POPUP_FILL, MAIN_MENU_ITEM_WIDTH, 0,
+		0, 0, 0);
+
+	popup_width_force(MAIN_MENU_FORCE_WIDTH);
+	game_menu_popup->cancel_item = resume_item;
+
+	result = popup_execute();
+
+	if (result == save_item) {
+		kernel.activate_menu = GAME_SAVE_MENU;
+	} else if (result == restore_item) {
+		kernel.activate_menu = GAME_RESTORE_MENU;
+	} else if (result == options_item) {
+		kernel.activate_menu = GAME_OPTIONS_MENU;
+	} else if (result == score_item) {
+		kernel.activate_menu = GAME_SCORE_MENU;
+	} else {
+		kernel.activate_menu = GAME_NO_MENU;
+	}
+
+	if (result == quit_item)
+		game.going = false;
+
+	switch (game_menu_popup->key) {
+	case alt_q_key:
+	case alt_x_key:
+	case ctrl_q_key:
+		game.going = false;
+		kernel.activate_menu = GAME_NO_MENU;
+		break;
+
+	case f5_key:
+		kernel.activate_menu = GAME_SAVE_MENU;
+		break;
+
+	case f7_key:
+		kernel.activate_menu = GAME_RESTORE_MENU;
+		break;
+
+	case f8_key:
+		kernel.activate_menu = GAME_SCORE_MENU;
+		break;
+
+	case f10_key:
+		kernel.activate_menu = GAME_OPTIONS_MENU;
+		break;
+
+	default:
+		break;
+	}
+#endif
+}
+
+void global_game_menu() {
+	bool loaded = false;
+
+	if (box_param.series == NULL) {
+		Common::strcpy_s(box_param.name, "*BOX");
+		if (popup_box_load())
+			goto done;
+		loaded = true;
+	}
+
+	game_menu_setup();
+	g_engine->flushKeys();
+
+	do {
+		switch (kernel.activate_menu) {
+		case GAME_MAIN_MENU:
+			global_menu_main();
+			break;
+		case GAME_SAVE_MENU:
+			if (config_file.original_save_load) {
+				global_menu_save_restore(true);
+			} else {
+				kernel.activate_menu = GAME_NO_MENU;
+				g_engine->saveGameDialog();
+			}
+			break;
+		case GAME_RESTORE_MENU:
+			if (config_file.original_save_load) {
+				global_menu_save_restore(false);
+			} else {
+				kernel.activate_menu = GAME_NO_MENU;
+				if (g_engine->loadGameDialog())
+					// Dummy name to flag that load was successful
+					Common::strcpy_s(save_game_buf, "OK");
+			}
+			break;
+		case GAME_OPTIONS_MENU:
+			global_menu_options();
+			break;
+		case GAME_DIFFICULTY_MENU:
+			global_menu_difficulty();
+			break;
+		case GAME_SCORE_MENU:
+			global_menu_score();
+			break;
+		default:
+			kernel.activate_menu = GAME_NO_MENU;
+			break;
+		}
+	} while (!g_engine->shouldQuit() && game.going && kernel.activate_menu != GAME_NO_MENU);
+
+	game_menu_shutdown();
+
+	if (loaded) {
+		sprite_free(&box_param.menu, true);
+		sprite_free(&box_param.logo, true);
+		sprite_free(&box_param.series, true);
+	}
+
+done:
+	;
+}
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/menus.h b/engines/mads/madsv2/forest/menus.h
new file mode 100644
index 00000000000..2b0635f7cb3
--- /dev/null
+++ b/engines/mads/madsv2/forest/menus.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_MENUS_H
+#define MADS_FOREST_MENUS_H
+
+#include "common/scummsys.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+
+extern void global_menu_system_init();
+extern void global_menu_system_shutdown();
+extern void global_emergency_save();
+extern void global_game_menu();
+
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room101.cpp b/engines/mads/madsv2/forest/rooms/room101.cpp
new file mode 100644
index 00000000000..204d289745d
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room101.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room101.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_101_init() {
+}
+
+static void room_101_daemon() {
+}
+
+static void room_101_pre_parser() {
+}
+
+static void room_101_parser() {
+}
+
+void room_101_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_101_preload() {
+	room_init_code_pointer = room_101_init;
+	room_pre_parser_code_pointer = room_101_pre_parser;
+	room_parser_code_pointer = room_101_parser;
+	room_daemon_code_pointer = room_101_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room101.h b/engines/mads/madsv2/forest/rooms/room101.h
new file mode 100644
index 00000000000..ac8e31fd121
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room101.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_101_H
+#define MADS_FOREST_ROOMS_101_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_101_preload();
+extern void room_101_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room103.cpp b/engines/mads/madsv2/forest/rooms/room103.cpp
new file mode 100644
index 00000000000..a019225a50d
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room103.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room103.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_103_init() {
+}
+
+static void room_103_daemon() {
+}
+
+static void room_103_pre_parser() {
+}
+
+static void room_103_parser() {
+}
+
+void room_103_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_103_preload() {
+	room_init_code_pointer = room_103_init;
+	room_pre_parser_code_pointer = room_103_pre_parser;
+	room_parser_code_pointer = room_103_parser;
+	room_daemon_code_pointer = room_103_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room103.h b/engines/mads/madsv2/forest/rooms/room103.h
new file mode 100644
index 00000000000..78fb07904e4
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room103.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_103_H
+#define MADS_FOREST_ROOMS_103_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_103_preload();
+extern void room_103_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room104.cpp b/engines/mads/madsv2/forest/rooms/room104.cpp
new file mode 100644
index 00000000000..680889df50d
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room104.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room104.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_104_init() {
+}
+
+static void room_104_daemon() {
+}
+
+static void room_104_pre_parser() {
+}
+
+static void room_104_parser() {
+}
+
+void room_104_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_104_preload() {
+	room_init_code_pointer = room_104_init;
+	room_pre_parser_code_pointer = room_104_pre_parser;
+	room_parser_code_pointer = room_104_parser;
+	room_daemon_code_pointer = room_104_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room104.h b/engines/mads/madsv2/forest/rooms/room104.h
new file mode 100644
index 00000000000..803eb127a36
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room104.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_104_H
+#define MADS_FOREST_ROOMS_104_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_104_preload();
+extern void room_104_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room106.cpp b/engines/mads/madsv2/forest/rooms/room106.cpp
new file mode 100644
index 00000000000..46646593123
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room106.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room106.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_106_init() {
+}
+
+static void room_106_daemon() {
+}
+
+static void room_106_pre_parser() {
+}
+
+static void room_106_parser() {
+}
+
+void room_106_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_106_preload() {
+	room_init_code_pointer = room_106_init;
+	room_pre_parser_code_pointer = room_106_pre_parser;
+	room_parser_code_pointer = room_106_parser;
+	room_daemon_code_pointer = room_106_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room106.h b/engines/mads/madsv2/forest/rooms/room106.h
new file mode 100644
index 00000000000..dffcf0c2682
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room106.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_106_H
+#define MADS_FOREST_ROOMS_106_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_106_preload();
+extern void room_106_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room107.cpp b/engines/mads/madsv2/forest/rooms/room107.cpp
new file mode 100644
index 00000000000..4e4075d4524
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room107.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room107.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_107_init() {
+}
+
+static void room_107_daemon() {
+}
+
+static void room_107_pre_parser() {
+}
+
+static void room_107_parser() {
+}
+
+void room_107_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_107_preload() {
+	room_init_code_pointer = room_107_init;
+	room_pre_parser_code_pointer = room_107_pre_parser;
+	room_parser_code_pointer = room_107_parser;
+	room_daemon_code_pointer = room_107_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room107.h b/engines/mads/madsv2/forest/rooms/room107.h
new file mode 100644
index 00000000000..3a733abbcd3
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room107.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_107_H
+#define MADS_FOREST_ROOMS_107_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_107_preload();
+extern void room_107_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room199.cpp b/engines/mads/madsv2/forest/rooms/room199.cpp
new file mode 100644
index 00000000000..1ad9e804b96
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room199.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room199.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_199_init() {
+}
+
+static void room_199_daemon() {
+}
+
+static void room_199_pre_parser() {
+}
+
+static void room_199_parser() {
+}
+
+void room_199_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_199_preload() {
+	room_init_code_pointer = room_199_init;
+	room_pre_parser_code_pointer = room_199_pre_parser;
+	room_parser_code_pointer = room_199_parser;
+	room_daemon_code_pointer = room_199_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room199.h b/engines/mads/madsv2/forest/rooms/room199.h
new file mode 100644
index 00000000000..a777a883676
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room199.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_199_H
+#define MADS_FOREST_ROOMS_199_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_199_preload();
+extern void room_199_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room201.cpp b/engines/mads/madsv2/forest/rooms/room201.cpp
new file mode 100644
index 00000000000..9fc82b87e5f
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room201.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room201.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_201_init() {
+}
+
+static void room_201_daemon() {
+}
+
+static void room_201_pre_parser() {
+}
+
+static void room_201_parser() {
+}
+
+void room_201_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_201_preload() {
+	room_init_code_pointer = room_201_init;
+	room_pre_parser_code_pointer = room_201_pre_parser;
+	room_parser_code_pointer = room_201_parser;
+	room_daemon_code_pointer = room_201_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room201.h b/engines/mads/madsv2/forest/rooms/room201.h
new file mode 100644
index 00000000000..3d452fa6f65
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room201.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_201_H
+#define MADS_FOREST_ROOMS_201_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_201_preload();
+extern void room_201_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room203.cpp b/engines/mads/madsv2/forest/rooms/room203.cpp
new file mode 100644
index 00000000000..7dba244347d
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room203.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room203.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_203_init() {
+}
+
+static void room_203_daemon() {
+}
+
+static void room_203_pre_parser() {
+}
+
+static void room_203_parser() {
+}
+
+void room_203_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_203_preload() {
+	room_init_code_pointer = room_203_init;
+	room_pre_parser_code_pointer = room_203_pre_parser;
+	room_parser_code_pointer = room_203_parser;
+	room_daemon_code_pointer = room_203_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room203.h b/engines/mads/madsv2/forest/rooms/room203.h
new file mode 100644
index 00000000000..f41afaf244b
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room203.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_203_H
+#define MADS_FOREST_ROOMS_203_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_203_preload();
+extern void room_203_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room204.cpp b/engines/mads/madsv2/forest/rooms/room204.cpp
new file mode 100644
index 00000000000..a3265ab6f87
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room204.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room204.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_204_init() {
+}
+
+static void room_204_daemon() {
+}
+
+static void room_204_pre_parser() {
+}
+
+static void room_204_parser() {
+}
+
+void room_204_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_204_preload() {
+	room_init_code_pointer = room_204_init;
+	room_pre_parser_code_pointer = room_204_pre_parser;
+	room_parser_code_pointer = room_204_parser;
+	room_daemon_code_pointer = room_204_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room204.h b/engines/mads/madsv2/forest/rooms/room204.h
new file mode 100644
index 00000000000..5b60edffc52
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room204.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_204_H
+#define MADS_FOREST_ROOMS_204_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_204_preload();
+extern void room_204_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room205.cpp b/engines/mads/madsv2/forest/rooms/room205.cpp
new file mode 100644
index 00000000000..f0ba29ce313
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room205.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room205.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_205_init() {
+}
+
+static void room_205_daemon() {
+}
+
+static void room_205_pre_parser() {
+}
+
+static void room_205_parser() {
+}
+
+void room_205_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_205_preload() {
+	room_init_code_pointer = room_205_init;
+	room_pre_parser_code_pointer = room_205_pre_parser;
+	room_parser_code_pointer = room_205_parser;
+	room_daemon_code_pointer = room_205_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room205.h b/engines/mads/madsv2/forest/rooms/room205.h
new file mode 100644
index 00000000000..5c961acdbff
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room205.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_205_H
+#define MADS_FOREST_ROOMS_205_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_205_preload();
+extern void room_205_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room210.cpp b/engines/mads/madsv2/forest/rooms/room210.cpp
new file mode 100644
index 00000000000..ae17ec3faae
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room210.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room210.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_210_init() {
+}
+
+static void room_210_daemon() {
+}
+
+static void room_210_pre_parser() {
+}
+
+static void room_210_parser() {
+}
+
+void room_210_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_210_preload() {
+	room_init_code_pointer = room_210_init;
+	room_pre_parser_code_pointer = room_210_pre_parser;
+	room_parser_code_pointer = room_210_parser;
+	room_daemon_code_pointer = room_210_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room210.h b/engines/mads/madsv2/forest/rooms/room210.h
new file mode 100644
index 00000000000..e33943b7497
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room210.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_210_H
+#define MADS_FOREST_ROOMS_210_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_210_preload();
+extern void room_210_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room211.cpp b/engines/mads/madsv2/forest/rooms/room211.cpp
new file mode 100644
index 00000000000..8173536b2dd
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room211.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room211.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_211_init() {
+}
+
+static void room_211_daemon() {
+}
+
+static void room_211_pre_parser() {
+}
+
+static void room_211_parser() {
+}
+
+void room_211_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_211_preload() {
+	room_init_code_pointer = room_211_init;
+	room_pre_parser_code_pointer = room_211_pre_parser;
+	room_parser_code_pointer = room_211_parser;
+	room_daemon_code_pointer = room_211_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room211.h b/engines/mads/madsv2/forest/rooms/room211.h
new file mode 100644
index 00000000000..acd4d55057d
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room211.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_211_H
+#define MADS_FOREST_ROOMS_211_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_211_preload();
+extern void room_211_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room220.cpp b/engines/mads/madsv2/forest/rooms/room220.cpp
new file mode 100644
index 00000000000..83679c727bc
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room220.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room220.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_220_init() {
+}
+
+static void room_220_daemon() {
+}
+
+static void room_220_pre_parser() {
+}
+
+static void room_220_parser() {
+}
+
+void room_220_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_220_preload() {
+	room_init_code_pointer = room_220_init;
+	room_pre_parser_code_pointer = room_220_pre_parser;
+	room_parser_code_pointer = room_220_parser;
+	room_daemon_code_pointer = room_220_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room220.h b/engines/mads/madsv2/forest/rooms/room220.h
new file mode 100644
index 00000000000..eefc984dede
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room220.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_220_H
+#define MADS_FOREST_ROOMS_220_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_220_preload();
+extern void room_220_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room221.cpp b/engines/mads/madsv2/forest/rooms/room221.cpp
new file mode 100644
index 00000000000..81bb0747d0a
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room221.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room221.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_221_init() {
+}
+
+static void room_221_daemon() {
+}
+
+static void room_221_pre_parser() {
+}
+
+static void room_221_parser() {
+}
+
+void room_221_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_221_preload() {
+	room_init_code_pointer = room_221_init;
+	room_pre_parser_code_pointer = room_221_pre_parser;
+	room_parser_code_pointer = room_221_parser;
+	room_daemon_code_pointer = room_221_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room221.h b/engines/mads/madsv2/forest/rooms/room221.h
new file mode 100644
index 00000000000..727083335cf
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room221.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_221_H
+#define MADS_FOREST_ROOMS_221_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_221_preload();
+extern void room_221_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room301.cpp b/engines/mads/madsv2/forest/rooms/room301.cpp
new file mode 100644
index 00000000000..bc879fa1355
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room301.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room301.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_301_init() {
+}
+
+static void room_301_daemon() {
+}
+
+static void room_301_pre_parser() {
+}
+
+static void room_301_parser() {
+}
+
+void room_301_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_301_preload() {
+	room_init_code_pointer = room_301_init;
+	room_pre_parser_code_pointer = room_301_pre_parser;
+	room_parser_code_pointer = room_301_parser;
+	room_daemon_code_pointer = room_301_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room301.h b/engines/mads/madsv2/forest/rooms/room301.h
new file mode 100644
index 00000000000..3b4d6a2ad63
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room301.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_301_H
+#define MADS_FOREST_ROOMS_301_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_301_preload();
+extern void room_301_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room302.cpp b/engines/mads/madsv2/forest/rooms/room302.cpp
new file mode 100644
index 00000000000..fe16ebcf227
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room302.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room302.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_302_init() {
+}
+
+static void room_302_daemon() {
+}
+
+static void room_302_pre_parser() {
+}
+
+static void room_302_parser() {
+}
+
+void room_302_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_302_preload() {
+	room_init_code_pointer = room_302_init;
+	room_pre_parser_code_pointer = room_302_pre_parser;
+	room_parser_code_pointer = room_302_parser;
+	room_daemon_code_pointer = room_302_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room302.h b/engines/mads/madsv2/forest/rooms/room302.h
new file mode 100644
index 00000000000..3954e480767
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room302.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_302_H
+#define MADS_FOREST_ROOMS_302_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_302_preload();
+extern void room_302_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room303.cpp b/engines/mads/madsv2/forest/rooms/room303.cpp
new file mode 100644
index 00000000000..6d476f668ff
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room303.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room303.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_303_init() {
+}
+
+static void room_303_daemon() {
+}
+
+static void room_303_pre_parser() {
+}
+
+static void room_303_parser() {
+}
+
+void room_303_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_303_preload() {
+	room_init_code_pointer = room_303_init;
+	room_pre_parser_code_pointer = room_303_pre_parser;
+	room_parser_code_pointer = room_303_parser;
+	room_daemon_code_pointer = room_303_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room303.h b/engines/mads/madsv2/forest/rooms/room303.h
new file mode 100644
index 00000000000..ee4b4ef75fa
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room303.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_303_H
+#define MADS_FOREST_ROOMS_303_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_303_preload();
+extern void room_303_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room304.cpp b/engines/mads/madsv2/forest/rooms/room304.cpp
new file mode 100644
index 00000000000..105c64b1714
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room304.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room304.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_304_init() {
+}
+
+static void room_304_daemon() {
+}
+
+static void room_304_pre_parser() {
+}
+
+static void room_304_parser() {
+}
+
+void room_304_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_304_preload() {
+	room_init_code_pointer = room_304_init;
+	room_pre_parser_code_pointer = room_304_pre_parser;
+	room_parser_code_pointer = room_304_parser;
+	room_daemon_code_pointer = room_304_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room304.h b/engines/mads/madsv2/forest/rooms/room304.h
new file mode 100644
index 00000000000..bbc85359cb4
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room304.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_304_H
+#define MADS_FOREST_ROOMS_304_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_304_preload();
+extern void room_304_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room305.cpp b/engines/mads/madsv2/forest/rooms/room305.cpp
new file mode 100644
index 00000000000..b789bcd6474
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room305.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room305.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_305_init() {
+}
+
+static void room_305_daemon() {
+}
+
+static void room_305_pre_parser() {
+}
+
+static void room_305_parser() {
+}
+
+void room_305_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_305_preload() {
+	room_init_code_pointer = room_305_init;
+	room_pre_parser_code_pointer = room_305_pre_parser;
+	room_parser_code_pointer = room_305_parser;
+	room_daemon_code_pointer = room_305_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room305.h b/engines/mads/madsv2/forest/rooms/room305.h
new file mode 100644
index 00000000000..1a55b1d83a2
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room305.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_305_H
+#define MADS_FOREST_ROOMS_305_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_305_preload();
+extern void room_305_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room306.cpp b/engines/mads/madsv2/forest/rooms/room306.cpp
new file mode 100644
index 00000000000..27fbacdc6cb
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room306.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room306.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_306_init() {
+}
+
+static void room_306_daemon() {
+}
+
+static void room_306_pre_parser() {
+}
+
+static void room_306_parser() {
+}
+
+void room_306_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_306_preload() {
+	room_init_code_pointer = room_306_init;
+	room_pre_parser_code_pointer = room_306_pre_parser;
+	room_parser_code_pointer = room_306_parser;
+	room_daemon_code_pointer = room_306_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room306.h b/engines/mads/madsv2/forest/rooms/room306.h
new file mode 100644
index 00000000000..57f2e70ff9b
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room306.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_306_H
+#define MADS_FOREST_ROOMS_306_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_306_preload();
+extern void room_306_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room307.cpp b/engines/mads/madsv2/forest/rooms/room307.cpp
new file mode 100644
index 00000000000..409ed029d8b
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room307.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room307.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_307_init() {
+}
+
+static void room_307_daemon() {
+}
+
+static void room_307_pre_parser() {
+}
+
+static void room_307_parser() {
+}
+
+void room_307_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_307_preload() {
+	room_init_code_pointer = room_307_init;
+	room_pre_parser_code_pointer = room_307_pre_parser;
+	room_parser_code_pointer = room_307_parser;
+	room_daemon_code_pointer = room_307_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room307.h b/engines/mads/madsv2/forest/rooms/room307.h
new file mode 100644
index 00000000000..e303654ec9f
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room307.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_307_H
+#define MADS_FOREST_ROOMS_307_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_307_preload();
+extern void room_307_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room308.cpp b/engines/mads/madsv2/forest/rooms/room308.cpp
new file mode 100644
index 00000000000..8555f6dbd6d
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room308.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room308.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_308_init() {
+}
+
+static void room_308_daemon() {
+}
+
+static void room_308_pre_parser() {
+}
+
+static void room_308_parser() {
+}
+
+void room_308_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_308_preload() {
+	room_init_code_pointer = room_308_init;
+	room_pre_parser_code_pointer = room_308_pre_parser;
+	room_parser_code_pointer = room_308_parser;
+	room_daemon_code_pointer = room_308_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room308.h b/engines/mads/madsv2/forest/rooms/room308.h
new file mode 100644
index 00000000000..878bb459214
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room308.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_308_H
+#define MADS_FOREST_ROOMS_308_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_308_preload();
+extern void room_308_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room321.cpp b/engines/mads/madsv2/forest/rooms/room321.cpp
new file mode 100644
index 00000000000..7325c714f9f
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room321.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room321.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_321_init() {
+}
+
+static void room_321_daemon() {
+}
+
+static void room_321_pre_parser() {
+}
+
+static void room_321_parser() {
+}
+
+void room_321_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_321_preload() {
+	room_init_code_pointer = room_321_init;
+	room_pre_parser_code_pointer = room_321_pre_parser;
+	room_parser_code_pointer = room_321_parser;
+	room_daemon_code_pointer = room_321_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room321.h b/engines/mads/madsv2/forest/rooms/room321.h
new file mode 100644
index 00000000000..50d9b82d4ef
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room321.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_321_H
+#define MADS_FOREST_ROOMS_321_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_321_preload();
+extern void room_321_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room322.cpp b/engines/mads/madsv2/forest/rooms/room322.cpp
new file mode 100644
index 00000000000..07c0a80e45c
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room322.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room322.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_322_init() {
+}
+
+static void room_322_daemon() {
+}
+
+static void room_322_pre_parser() {
+}
+
+static void room_322_parser() {
+}
+
+void room_322_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_322_preload() {
+	room_init_code_pointer = room_322_init;
+	room_pre_parser_code_pointer = room_322_pre_parser;
+	room_parser_code_pointer = room_322_parser;
+	room_daemon_code_pointer = room_322_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room322.h b/engines/mads/madsv2/forest/rooms/room322.h
new file mode 100644
index 00000000000..7f17842cbfe
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room322.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_322_H
+#define MADS_FOREST_ROOMS_322_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_322_preload();
+extern void room_322_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room401.cpp b/engines/mads/madsv2/forest/rooms/room401.cpp
new file mode 100644
index 00000000000..626d2f7cb9e
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room401.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room401.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_401_init() {
+}
+
+static void room_401_daemon() {
+}
+
+static void room_401_pre_parser() {
+}
+
+static void room_401_parser() {
+}
+
+void room_401_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_401_preload() {
+	room_init_code_pointer = room_401_init;
+	room_pre_parser_code_pointer = room_401_pre_parser;
+	room_parser_code_pointer = room_401_parser;
+	room_daemon_code_pointer = room_401_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room401.h b/engines/mads/madsv2/forest/rooms/room401.h
new file mode 100644
index 00000000000..8a278732cb1
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room401.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_401_H
+#define MADS_FOREST_ROOMS_401_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_401_preload();
+extern void room_401_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room402.cpp b/engines/mads/madsv2/forest/rooms/room402.cpp
new file mode 100644
index 00000000000..34273aa93ce
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room402.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room402.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_402_init() {
+}
+
+static void room_402_daemon() {
+}
+
+static void room_402_pre_parser() {
+}
+
+static void room_402_parser() {
+}
+
+void room_402_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_402_preload() {
+	room_init_code_pointer = room_402_init;
+	room_pre_parser_code_pointer = room_402_pre_parser;
+	room_parser_code_pointer = room_402_parser;
+	room_daemon_code_pointer = room_402_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room402.h b/engines/mads/madsv2/forest/rooms/room402.h
new file mode 100644
index 00000000000..59b9629dd34
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room402.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_402_H
+#define MADS_FOREST_ROOMS_402_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_402_preload();
+extern void room_402_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room403.cpp b/engines/mads/madsv2/forest/rooms/room403.cpp
new file mode 100644
index 00000000000..35bcb54e03f
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room403.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room403.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_403_init() {
+}
+
+static void room_403_daemon() {
+}
+
+static void room_403_pre_parser() {
+}
+
+static void room_403_parser() {
+}
+
+void room_403_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_403_preload() {
+	room_init_code_pointer = room_403_init;
+	room_pre_parser_code_pointer = room_403_pre_parser;
+	room_parser_code_pointer = room_403_parser;
+	room_daemon_code_pointer = room_403_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room403.h b/engines/mads/madsv2/forest/rooms/room403.h
new file mode 100644
index 00000000000..a12e4c0cdf1
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room403.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_403_H
+#define MADS_FOREST_ROOMS_403_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_403_preload();
+extern void room_403_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room404.cpp b/engines/mads/madsv2/forest/rooms/room404.cpp
new file mode 100644
index 00000000000..a0e1dff1e99
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room404.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room404.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_404_init() {
+}
+
+static void room_404_daemon() {
+}
+
+static void room_404_pre_parser() {
+}
+
+static void room_404_parser() {
+}
+
+void room_404_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_404_preload() {
+	room_init_code_pointer = room_404_init;
+	room_pre_parser_code_pointer = room_404_pre_parser;
+	room_parser_code_pointer = room_404_parser;
+	room_daemon_code_pointer = room_404_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room404.h b/engines/mads/madsv2/forest/rooms/room404.h
new file mode 100644
index 00000000000..08b49e94a69
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room404.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_404_H
+#define MADS_FOREST_ROOMS_404_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_404_preload();
+extern void room_404_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room405.cpp b/engines/mads/madsv2/forest/rooms/room405.cpp
new file mode 100644
index 00000000000..794e67c6d6e
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room405.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room405.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_405_init() {
+}
+
+static void room_405_daemon() {
+}
+
+static void room_405_pre_parser() {
+}
+
+static void room_405_parser() {
+}
+
+void room_405_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_405_preload() {
+	room_init_code_pointer = room_405_init;
+	room_pre_parser_code_pointer = room_405_pre_parser;
+	room_parser_code_pointer = room_405_parser;
+	room_daemon_code_pointer = room_405_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room405.h b/engines/mads/madsv2/forest/rooms/room405.h
new file mode 100644
index 00000000000..ec0d4dacced
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room405.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_405_H
+#define MADS_FOREST_ROOMS_405_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_405_preload();
+extern void room_405_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room420.cpp b/engines/mads/madsv2/forest/rooms/room420.cpp
new file mode 100644
index 00000000000..e3ccd7685e7
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room420.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room420.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_420_init() {
+}
+
+static void room_420_daemon() {
+}
+
+static void room_420_pre_parser() {
+}
+
+static void room_420_parser() {
+}
+
+void room_420_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_420_preload() {
+	room_init_code_pointer = room_420_init;
+	room_pre_parser_code_pointer = room_420_pre_parser;
+	room_parser_code_pointer = room_420_parser;
+	room_daemon_code_pointer = room_420_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room420.h b/engines/mads/madsv2/forest/rooms/room420.h
new file mode 100644
index 00000000000..db92afc132e
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room420.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_420_H
+#define MADS_FOREST_ROOMS_420_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_420_preload();
+extern void room_420_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room501.cpp b/engines/mads/madsv2/forest/rooms/room501.cpp
new file mode 100644
index 00000000000..c2075289b60
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room501.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room501.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_501_init() {
+}
+
+static void room_501_daemon() {
+}
+
+static void room_501_pre_parser() {
+}
+
+static void room_501_parser() {
+}
+
+void room_501_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_501_preload() {
+	room_init_code_pointer = room_501_init;
+	room_pre_parser_code_pointer = room_501_pre_parser;
+	room_parser_code_pointer = room_501_parser;
+	room_daemon_code_pointer = room_501_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room501.h b/engines/mads/madsv2/forest/rooms/room501.h
new file mode 100644
index 00000000000..cf4c880bbf2
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room501.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_501_H
+#define MADS_FOREST_ROOMS_501_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_501_preload();
+extern void room_501_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room503.cpp b/engines/mads/madsv2/forest/rooms/room503.cpp
new file mode 100644
index 00000000000..0c66b325ff4
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room503.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room503.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_503_init() {
+}
+
+static void room_503_daemon() {
+}
+
+static void room_503_pre_parser() {
+}
+
+static void room_503_parser() {
+}
+
+void room_503_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_503_preload() {
+	room_init_code_pointer = room_503_init;
+	room_pre_parser_code_pointer = room_503_pre_parser;
+	room_parser_code_pointer = room_503_parser;
+	room_daemon_code_pointer = room_503_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room503.h b/engines/mads/madsv2/forest/rooms/room503.h
new file mode 100644
index 00000000000..e71795c42af
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room503.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_503_H
+#define MADS_FOREST_ROOMS_503_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_503_preload();
+extern void room_503_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room509.cpp b/engines/mads/madsv2/forest/rooms/room509.cpp
new file mode 100644
index 00000000000..50881aa9434
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room509.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room509.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_509_init() {
+}
+
+static void room_509_daemon() {
+}
+
+static void room_509_pre_parser() {
+}
+
+static void room_509_parser() {
+}
+
+void room_509_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_509_preload() {
+	room_init_code_pointer = room_509_init;
+	room_pre_parser_code_pointer = room_509_pre_parser;
+	room_parser_code_pointer = room_509_parser;
+	room_daemon_code_pointer = room_509_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room509.h b/engines/mads/madsv2/forest/rooms/room509.h
new file mode 100644
index 00000000000..b8db30bda3b
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room509.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_509_H
+#define MADS_FOREST_ROOMS_509_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_509_preload();
+extern void room_509_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room510.cpp b/engines/mads/madsv2/forest/rooms/room510.cpp
new file mode 100644
index 00000000000..815c6ed21e3
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room510.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room510.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_510_init() {
+}
+
+static void room_510_daemon() {
+}
+
+static void room_510_pre_parser() {
+}
+
+static void room_510_parser() {
+}
+
+void room_510_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_510_preload() {
+	room_init_code_pointer = room_510_init;
+	room_pre_parser_code_pointer = room_510_pre_parser;
+	room_parser_code_pointer = room_510_parser;
+	room_daemon_code_pointer = room_510_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room510.h b/engines/mads/madsv2/forest/rooms/room510.h
new file mode 100644
index 00000000000..eecc96b3ced
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room510.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_510_H
+#define MADS_FOREST_ROOMS_510_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_510_preload();
+extern void room_510_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room520.cpp b/engines/mads/madsv2/forest/rooms/room520.cpp
new file mode 100644
index 00000000000..f907dd7d515
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room520.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room520.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_520_init() {
+}
+
+static void room_520_daemon() {
+}
+
+static void room_520_pre_parser() {
+}
+
+static void room_520_parser() {
+}
+
+void room_520_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_520_preload() {
+	room_init_code_pointer = room_520_init;
+	room_pre_parser_code_pointer = room_520_pre_parser;
+	room_parser_code_pointer = room_520_parser;
+	room_daemon_code_pointer = room_520_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room520.h b/engines/mads/madsv2/forest/rooms/room520.h
new file mode 100644
index 00000000000..4f77e8f3e7b
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room520.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_520_H
+#define MADS_FOREST_ROOMS_520_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_520_preload();
+extern void room_520_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room901.cpp b/engines/mads/madsv2/forest/rooms/room901.cpp
new file mode 100644
index 00000000000..d712e4bf527
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room901.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room901.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_901_init() {
+}
+
+static void room_901_daemon() {
+}
+
+static void room_901_pre_parser() {
+}
+
+static void room_901_parser() {
+}
+
+void room_901_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_901_preload() {
+	room_init_code_pointer = room_901_init;
+	room_pre_parser_code_pointer = room_901_pre_parser;
+	room_parser_code_pointer = room_901_parser;
+	room_daemon_code_pointer = room_901_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room901.h b/engines/mads/madsv2/forest/rooms/room901.h
new file mode 100644
index 00000000000..dc4c9893ad5
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room901.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_901_H
+#define MADS_FOREST_ROOMS_901_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_901_preload();
+extern void room_901_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room903.cpp b/engines/mads/madsv2/forest/rooms/room903.cpp
new file mode 100644
index 00000000000..0cb0de40fa4
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room903.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room903.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};
+
+static Scratch scratch;
+
+#define local (&scratch)
+#define ss    local->sprite
+#define seq   local->sequence
+#define aa    local->animation
+
+
+static void room_903_init() {
+}
+
+static void room_903_daemon() {
+}
+
+static void room_903_pre_parser() {
+}
+
+static void room_903_parser() {
+}
+
+void room_903_synchronize(Common::Serializer &s) {
+	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
+	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
+	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+}
+
+void room_903_preload() {
+	room_init_code_pointer = room_903_init;
+	room_pre_parser_code_pointer = room_903_pre_parser;
+	room_parser_code_pointer = room_903_parser;
+	room_daemon_code_pointer = room_903_daemon;
+
+	section_1_walker();
+	section_1_interface();
+}
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/forest/rooms/room903.h b/engines/mads/madsv2/forest/rooms/room903.h
new file mode 100644
index 00000000000..6b49d960006
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room903.h
@@ -0,0 +1,41 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_FOREST_ROOMS_903_H
+#define MADS_FOREST_ROOMS_903_H
+
+#include "common/serializer.h"
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+extern void room_903_preload();
+extern void room_903_synchronize(Common::Serializer &s);
+
+} // namespace Rooms
+} // namespace Forest
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/madsv2/forest/rooms/room904.cpp b/engines/mads/madsv2/forest/rooms/room904.cpp
new file mode 100644
index 00000000000..90da7832d10
--- /dev/null
+++ b/engines/mads/madsv2/forest/rooms/room904.cpp
@@ -0,0 +1,91 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "mads/madsv2/core/conv.h"
+#include "mads/madsv2/core/game.h"
+#include "mads/madsv2/core/imath.h"
+#include "mads/madsv2/core/inter.h"
+#include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/sound.h"
+#include "mads/madsv2/core/text.h"
+#include "mads/madsv2/forest/mads/inventory.h"
+#include "mads/madsv2/forest/mads/sounds.h"
+#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/room904.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace Forest {
+namespace Rooms {
+
+struct Scratch {
+	int16 sprite[15];       /* Sprite series handles */
+	int16 sequence[15];     /* Sequence handles      */
+	int16 animation[4];     /* Animation handles     */
+
+	int16 dragon_frame;     /* frame animation is on */
+
+	int16 done_with_conv;   /* T if done with conv   */
+	int16 prev_room;
+};


Commit: 74ed86e494d28182d7df07bab68bc37cf5c52b96
    https://github.com/scummvm/scummvm/commit/74ed86e494d28182d7df07bab68bc37cf5c52b96
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-28T21:12:20+10:00

Commit Message:
MADS: FOREST: Implementing section 9 rooms

Changed paths:
    engines/mads/madsv2/forest/global.cpp
    engines/mads/madsv2/forest/global.h
    engines/mads/madsv2/forest/mads/sounds.h
    engines/mads/madsv2/forest/mads/words.h
    engines/mads/madsv2/forest/rooms/room901.cpp
    engines/mads/madsv2/forest/rooms/room903.cpp
    engines/mads/madsv2/forest/rooms/room904.cpp
    engines/mads/madsv2/forest/rooms/section9.cpp


diff --git a/engines/mads/madsv2/forest/global.cpp b/engines/mads/madsv2/forest/global.cpp
index 3d926fd63b1..28cd2afc830 100644
--- a/engines/mads/madsv2/forest/global.cpp
+++ b/engines/mads/madsv2/forest/global.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/textconsole.h"
 #include "mads/madsv2/forest/global.h"
 #include "mads/madsv2/core/game.h"
 #include "mads/madsv2/core/kernel.h"
@@ -161,6 +162,11 @@ void sync_room(Common::Serializer &s) {
 	}
 }
 
+void global_digi_play(int num) {
+	// TODO
+	warning("TODO: global_digi_play");
+}
+
 } // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/forest/global.h b/engines/mads/madsv2/forest/global.h
index b64a68b9aca..bf1498e4907 100644
--- a/engines/mads/madsv2/forest/global.h
+++ b/engines/mads/madsv2/forest/global.h
@@ -29,9 +29,18 @@ namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
+enum {
+	g009 =  9,
+	g010 = 10,
+	g016 = 16,
+	g017 = 17
+};
+
 extern void global_section_constructor();
 extern void sync_room(Common::Serializer &s);
 
+extern void global_digi_play(int num);
+
 } // namespace Forest
 } // namespace MADSV2
 } // namespace MADS
diff --git a/engines/mads/madsv2/forest/mads/sounds.h b/engines/mads/madsv2/forest/mads/sounds.h
index 84513db6c9d..184b0c02101 100644
--- a/engines/mads/madsv2/forest/mads/sounds.h
+++ b/engines/mads/madsv2/forest/mads/sounds.h
@@ -28,7 +28,13 @@ namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
-//enum {};
+enum {
+	// General (shared) sounds
+	N_AllFade = 1,
+	N_MusicFade = 3,
+	N_NoiseOff = 4,
+	N_NoiseFade = 5
+};
 
 } // namespace Forest
 } // namespace MADSV2
diff --git a/engines/mads/madsv2/forest/mads/words.h b/engines/mads/madsv2/forest/mads/words.h
index 8ba2af5b3ac..199d01ec063 100644
--- a/engines/mads/madsv2/forest/mads/words.h
+++ b/engines/mads/madsv2/forest/mads/words.h
@@ -28,7 +28,9 @@ namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 
-//enum {};
+enum {
+	dummy = 0
+};
 
 } // namespace Forest
 } // namespace MADSV2
diff --git a/engines/mads/madsv2/forest/rooms/room901.cpp b/engines/mads/madsv2/forest/rooms/room901.cpp
index d712e4bf527..0a3a52d6128 100644
--- a/engines/mads/madsv2/forest/rooms/room901.cpp
+++ b/engines/mads/madsv2/forest/rooms/room901.cpp
@@ -19,60 +19,59 @@
  *
  */
 
+#include "common/config-manager.h"
 #include "mads/madsv2/core/conv.h"
 #include "mads/madsv2/core/game.h"
-#include "mads/madsv2/core/imath.h"
-#include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
-#include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/forest/rooms/section9.h"
 #include "mads/madsv2/forest/rooms/room901.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/forest.h"
 
 namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 namespace Rooms {
 
-struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
-};
-
-static Scratch scratch;
-
-#define local (&scratch)
-#define ss    local->sprite
-#define seq   local->sequence
-#define aa    local->animation
-
+/* Triggers */
+#define START_INTRO  100
 
 static void room_901_init() {
+	global[g009] = 0;
+	global[g010] = 0;
+	player.walker_visible = false;
+	viewing_at_y = 22;
+	mouse_hide();
+
+	if (ConfMan.getBool("seen_intro")) {
+		new_room = 904;
+	} else {
+		ConfMan.setBool("seen_intro", true);
+		ConfMan.flushToDisk();
+
+		kernel_timing_trigger(300, START_INTRO);
+	}
 }
 
 static void room_901_daemon() {
+	if (mouse_any_stroke || g_engine->hasPendingKey() || kernel.trigger == START_INTRO) {
+		g_engine->flushKeys();
+		mouse_hide();
+		new_room = 903;
+	}
 }
 
 static void room_901_pre_parser() {
+	new_room = 903;
 }
 
 static void room_901_parser() {
 }
 
 void room_901_synchronize(Common::Serializer &s) {
-	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
-	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
-	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	// Room has no scratch area
 }
 
 void room_901_preload() {
@@ -81,8 +80,10 @@ void room_901_preload() {
 	room_parser_code_pointer = room_901_parser;
 	room_daemon_code_pointer = room_901_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global[g016] = true;
+	mouse_hide();
+	section_9_walker();
+	section_9_interface();
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room903.cpp b/engines/mads/madsv2/forest/rooms/room903.cpp
index 0cb0de40fa4..34444129e99 100644
--- a/engines/mads/madsv2/forest/rooms/room903.cpp
+++ b/engines/mads/madsv2/forest/rooms/room903.cpp
@@ -24,55 +24,58 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
-#include "mads/madsv2/core/sound.h"
-#include "mads/madsv2/core/text.h"
-#include "mads/madsv2/forest/mads/inventory.h"
-#include "mads/madsv2/forest/mads/sounds.h"
-#include "mads/madsv2/forest/mads/words.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/section9.h"
 #include "mads/madsv2/forest/rooms/room903.h"
+#include "mads/madsv2/forest/global.h"
+#include "mads/madsv2/forest/forest.h"
 
 namespace MADS {
 namespace MADSV2 {
 namespace Forest {
 namespace Rooms {
 
-struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
-};
-
-static Scratch scratch;
-
 #define local (&scratch)
 #define ss    local->sprite
 #define seq   local->sequence
 #define aa    local->animation
 
+/* Triggers */
+#define TRIGGER0 100
+#define TRIGGER1 101
+
 
 static void room_903_init() {
+	mouse_hide();
+	global[g009] = false;
+	global[g010] = false;
+	viewing_at_y = 22;
+	kernel_timing_trigger(20, TRIGGER1);
+	kernel_run_animation("*rm903i", TRIGGER0);
+	player.walker_visible = false;
 }
 
 static void room_903_daemon() {
+	if (kernel.trigger == TRIGGER1)
+		global_digi_play(14);
+
+	if (mouse_any_stroke || g_engine->hasPendingKey() || kernel.trigger == TRIGGER0) {
+		g_engine->flushKeys();
+		new_room = 904;
+	}
 }
 
 static void room_903_pre_parser() {
+	new_room = 904;
 }
 
 static void room_903_parser() {
 }
 
 void room_903_synchronize(Common::Serializer &s) {
-	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
-	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
-	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	// Room has no scratch area
 }
 
 void room_903_preload() {
@@ -81,8 +84,9 @@ void room_903_preload() {
 	room_parser_code_pointer = room_903_parser;
 	room_daemon_code_pointer = room_903_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global[g016] = true;
+	section_9_walker();
+	section_9_interface();
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/room904.cpp b/engines/mads/madsv2/forest/rooms/room904.cpp
index 90da7832d10..11d6e17e8c5 100644
--- a/engines/mads/madsv2/forest/rooms/room904.cpp
+++ b/engines/mads/madsv2/forest/rooms/room904.cpp
@@ -24,13 +24,15 @@
 #include "mads/madsv2/core/imath.h"
 #include "mads/madsv2/core/inter.h"
 #include "mads/madsv2/core/kernel.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/mouse.h"
 #include "mads/madsv2/core/sound.h"
 #include "mads/madsv2/core/text.h"
 #include "mads/madsv2/forest/mads/inventory.h"
 #include "mads/madsv2/forest/mads/sounds.h"
 #include "mads/madsv2/forest/mads/words.h"
 #include "mads/madsv2/forest/global.h"
-#include "mads/madsv2/forest/rooms/section1.h"
+#include "mads/madsv2/forest/rooms/section9.h"
 #include "mads/madsv2/forest/rooms/room904.h"
 
 namespace MADS {
@@ -38,29 +40,23 @@ namespace MADSV2 {
 namespace Forest {
 namespace Rooms {
 
-struct Scratch {
-	int16 sprite[15];       /* Sprite series handles */
-	int16 sequence[15];     /* Sequence handles      */
-	int16 animation[4];     /* Animation handles     */
-
-	int16 dragon_frame;     /* frame animation is on */
-
-	int16 done_with_conv;   /* T if done with conv   */
-	int16 prev_room;
-};
-
-static Scratch scratch;
-
-#define local (&scratch)
-#define ss    local->sprite
-#define seq   local->sequence
-#define aa    local->animation
+/* Triggers */
+#define TRIGGER0  100
+#define TRIGGER1  101
 
 
 static void room_904_init() {
+	mouse_hide();
+	global[g009] = false;
+	global[g010] = false;
+	viewing_at_y = 22;
+	kernel_timing_trigger(20, TRIGGER1);
+	kernel_run_animation("*rm903i", TRIGGER0);
+	player.walker_visible = false;
 }
 
 static void room_904_daemon() {
+	// TODO: room_904_daemon
 }
 
 static void room_904_pre_parser() {
@@ -70,9 +66,7 @@ static void room_904_parser() {
 }
 
 void room_904_synchronize(Common::Serializer &s) {
-	for (int16 &v : scratch.sprite)    s.syncAsSint16LE(v);
-	for (int16 &v : scratch.sequence)  s.syncAsSint16LE(v);
-	for (int16 &v : scratch.animation) s.syncAsSint16LE(v);
+	// Room has no scratch area
 }
 
 void room_904_preload() {
@@ -81,8 +75,10 @@ void room_904_preload() {
 	room_parser_code_pointer = room_904_parser;
 	room_daemon_code_pointer = room_904_daemon;
 
-	section_1_walker();
-	section_1_interface();
+	global[g016] = true;
+	kernel.activate_menu = false;
+	section_9_walker();
+	section_9_interface();
 }
 
 } // namespace Rooms
diff --git a/engines/mads/madsv2/forest/rooms/section9.cpp b/engines/mads/madsv2/forest/rooms/section9.cpp
index bada0e7ed05..068482fe958 100644
--- a/engines/mads/madsv2/forest/rooms/section9.cpp
+++ b/engines/mads/madsv2/forest/rooms/section9.cpp
@@ -44,9 +44,24 @@ void section_9_init() {
 }
 
 void section_9_walker() {
+	char temp_buf[80];
+	Common::strcpy_s(temp_buf, player.series_name);
+	global[g017] = true;
+
+	if (!global[g016]) {
+		*player.series_name = '\0';
+	} else if (!player.force_series) {
+		Common::strcpy_s(player.series_name, "B");
+	}
+
+	if (strcmp(temp_buf, player.series_name) != 0)
+		player.walker_must_reload = true;
+	player.scaling_velocity = true;
 }
 
 void section_9_interface() {
+	Common::strcpy_s(kernel.interface, kernel_interface_name(0));
+	pal_change_color(254, 56, 47, 32);
 }
 
 void section_9_music() {


Commit: a57e2b6ae1546f3b44b6725d0d18bad7bb2c9dbe
    https://github.com/scummvm/scummvm/commit/a57e2b6ae1546f3b44b6725d0d18bad7bb2c9dbe
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-28T21:12:20+10:00

Commit Message:
MADS: DRAGONSPHERE: Warning fixes

Changed paths:
    engines/mads/madsv2/dragonsphere/rooms/room205.cpp
    engines/mads/madsv2/dragonsphere/rooms/room404.cpp
    engines/mads/madsv2/dragonsphere/rooms/section4.cpp


diff --git a/engines/mads/madsv2/dragonsphere/rooms/room205.cpp b/engines/mads/madsv2/dragonsphere/rooms/room205.cpp
index f4c5f506a53..852b15aac20 100644
--- a/engines/mads/madsv2/dragonsphere/rooms/room205.cpp
+++ b/engines/mads/madsv2/dragonsphere/rooms/room205.cpp
@@ -496,7 +496,7 @@ static void room_205_pre_parser() {
 	}
 
 	if (player_said_2(walk_to, shifter_village) ||
-	    player_said_2(walk_across, ground) && inter_point_y < 117) {
+	    (player_said_2(walk_across, ground) && inter_point_y < 117)) {
 		if (global[slime_healed]) {
 			switch (kernel.trigger) {
 			case 0:
@@ -611,7 +611,7 @@ static void room_205_parser() {
 	}
 
 	if (player_said_2(walk_to, shifter_village) ||
-	    player_said_2(walk_across, ground) && inter_point_y < 117) {
+	    (player_said_2(walk_across, ground) && inter_point_y < 117)) {
 		if (global[slime_healed]) {
 			new_room = 206;
 
diff --git a/engines/mads/madsv2/dragonsphere/rooms/room404.cpp b/engines/mads/madsv2/dragonsphere/rooms/room404.cpp
index 35a5089786b..5e599078061 100644
--- a/engines/mads/madsv2/dragonsphere/rooms/room404.cpp
+++ b/engines/mads/madsv2/dragonsphere/rooms/room404.cpp
@@ -302,7 +302,8 @@ static void room_404_init() {
 	if (global[desert_counter] == 6) {
 		aa[1] = kernel_run_animation(kernel_name('d', 1), 1);
 		local->anim_1_running = true;
-		kernel_reset_animation(aa[25], 30);
+		// WORKAROUND: Invalid line from the original commented out
+		//kernel_reset_animation(aa[25], 30);
 
 	} else if (previous_room == 120 || previous_room != KERNEL_RESTORING_GAME) {
 		ss[fx_dune_1] = kernel_load_series(kernel_name('x', 0), false);
diff --git a/engines/mads/madsv2/dragonsphere/rooms/section4.cpp b/engines/mads/madsv2/dragonsphere/rooms/section4.cpp
index d0affc84fd9..0801715b4c7 100644
--- a/engines/mads/madsv2/dragonsphere/rooms/section4.cpp
+++ b/engines/mads/madsv2/dragonsphere/rooms/section4.cpp
@@ -63,7 +63,7 @@ void section_4_walker() {
 
 	Common::strcpy_s(temp_buf, player.series_name);
 
-	if (new_room == 401 || new_room == 402 || new_room == 403 && global[player_persona] == PLAYER_IS_KING) {
+	if (new_room == 401 || new_room == 402 || (new_room == 403 && global[player_persona] == PLAYER_IS_KING)) {
 		global[perform_displacements] = false;
 	} else {
 		global[perform_displacements] = true;


Commit: e51035f2e79f21ab2b8d1c3536b4c9c2986c7f62
    https://github.com/scummvm/scummvm/commit/e51035f2e79f21ab2b8d1c3536b4c9c2986c7f62
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-28T21:12:21+10:00

Commit Message:
MADS: DRAGONSPHERE: Fix re-entering Caliph's tent

Changed paths:
    engines/mads/madsv2/dragonsphere/mads/conv.h


diff --git a/engines/mads/madsv2/dragonsphere/mads/conv.h b/engines/mads/madsv2/dragonsphere/mads/conv.h
index eaad7c58914..46ef64ef0b4 100644
--- a/engines/mads/madsv2/dragonsphere/mads/conv.h
+++ b/engines/mads/madsv2/dragonsphere/mads/conv.h
@@ -264,9 +264,9 @@ enum {
 };
 
 enum {
-	conv040_enter_b_b        =  4,
+	conv040_leave_b_b        =  4,
 	conv040_repeater         =  7,
-	conv040_leave_b_b        =  8
+	conv040_enter_b_b        =  8
 };
 
 enum {




More information about the Scummvm-git-logs mailing list