[Scummvm-git-logs] scummvm master -> 3d306a1e386cf8685b2cdbdd3939ee518aa46847

dreammaster noreply at scummvm.org
Tue May 19 09:51:35 UTC 2026


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

Summary:
b9f56221c1 MADS: PHANTOM: Implement main menu Resume button
82d619cab1 MADS: PHANTOM: Beginnings of textview namespace
3d306a1e38 MADS: PHANTOM: Further textview implementation


Commit: b9f56221c11185ddfa480782f304813d90702839
    https://github.com/scummvm/scummvm/commit/b9f56221c11185ddfa480782f304813d90702839
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-19T19:50:56+10:00

Commit Message:
MADS: PHANTOM: Implement main menu Resume button

Changed paths:
    engines/mads/madsv2/core/game.cpp
    engines/mads/madsv2/core/game.h
    engines/mads/madsv2/engine.cpp
    engines/mads/madsv2/phantom/main.cpp
    engines/mads/madsv2/phantom/menus.cpp


diff --git a/engines/mads/madsv2/core/game.cpp b/engines/mads/madsv2/core/game.cpp
index 96fb0026e92..a81c5827735 100644
--- a/engines/mads/madsv2/core/game.cpp
+++ b/engines/mads/madsv2/core/game.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "common/config-manager.h"
 #include "common/debug.h"
 #include "mads/madsv2/engine.h"
 #include "mads/madsv2/core/game.h"
@@ -84,6 +83,7 @@ char config_file_name[20];
 int win_status = WIN_NOTHING;
 
 int art_hags_are_on_hd;
+int savegame_slot = -1;
 byte game_restore_flag = false;         /* Flag if restoring game */
 byte game_autosaved = false;         /* Flag if autosaved      */
 byte game_mouse_cursor_fix = false;     /* Use special cursor fix */
@@ -1215,11 +1215,11 @@ void game_control() {
 
 	conv_system_init();
 
-	if (ConfMan.hasKey("save_slot")) {
+	if (savegame_slot != -1) {
 		// Flag to do a savegame load
 		game_restore_flag = 1;
 
-	} else {
+	} else if (!game_restore_flag) {
 		result = main_copy_verify();
 		if (result == COPY_FAIL) {
 			game.going = false;
@@ -1278,8 +1278,8 @@ void game_control() {
 				force_chain = true;
 			}
 		} else {
-			// Savegame load from GMM
-			g_engine->loadGameState(ConfMan.getInt("save_slot"));
+			// Savegame load from GMM or Resume from main menu
+			g_engine->loadGameState(savegame_slot);
 		}
 	}
 
@@ -3017,6 +3017,7 @@ int main_copy_verify() {
 void init_game() {
 	win_status = WIN_NOTHING;
 	game_restore_flag = false;
+	savegame_slot = -1;
 	game_autosaved = false;
 	game_mouse_cursor_fix = false;
 	abort_value = 0;
diff --git a/engines/mads/madsv2/core/game.h b/engines/mads/madsv2/core/game.h
index 026904ce73d..c98531586d9 100644
--- a/engines/mads/madsv2/core/game.h
+++ b/engines/mads/madsv2/core/game.h
@@ -159,6 +159,7 @@ enum {
 
 extern byte game_restore_flag;         /* Flag if restoring game */
 extern byte game_autosaved;            /* Flag if autosaved      */
+extern int savegame_slot;
 
 extern byte game_mouse_cursor_fix;     /* Mouse cursor fix       */
 
diff --git a/engines/mads/madsv2/engine.cpp b/engines/mads/madsv2/engine.cpp
index 090b70f017e..3ad4b61544b 100644
--- a/engines/mads/madsv2/engine.cpp
+++ b/engines/mads/madsv2/engine.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/system.h"
+#include "common/config-manager.h"
 #include "common/memstream.h"
 #include "engines/util.h"
 #include "mads/mads.h"
@@ -120,6 +121,9 @@ void MADSV2Engine::readConfigFile() {
 	_musicFlag = config_file.music_flag;
 	_soundFlag = config_file.sound_flag;
 	_speechFlag = config_file.speech_flag;
+
+	if (ConfMan.hasKey("save_slot"))
+		savegame_slot = ConfMan.getInt("save_slot");
 }
 
 bool MADSV2Engine::canLoadGameStateCurrently(Common::U32String *msg) {
diff --git a/engines/mads/madsv2/phantom/main.cpp b/engines/mads/madsv2/phantom/main.cpp
index 55de9ef4128..37fbaee4a48 100644
--- a/engines/mads/madsv2/phantom/main.cpp
+++ b/engines/mads/madsv2/phantom/main.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/config-manager.h"
+#include "gui/saveload.h"
 #include "mads/madsv2/phantom/main.h"
 #include "mads/madsv2/animview/animview.h"
 #include "mads/madsv2/core/env.h"
@@ -277,6 +278,24 @@ void phantom_main() {
 			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("@phantom");
 			selected_item = -1;
@@ -286,7 +305,12 @@ void phantom_main() {
 			// Exit
 			return;
 
+		case 6:
+			// Quotes
+			break;
+
 		default:
+			// Credits
 			break;
 		}
 	}
diff --git a/engines/mads/madsv2/phantom/menus.cpp b/engines/mads/madsv2/phantom/menus.cpp
index da9bb1d1915..0f9ad84599e 100644
--- a/engines/mads/madsv2/phantom/menus.cpp
+++ b/engines/mads/madsv2/phantom/menus.cpp
@@ -330,6 +330,10 @@ static void global_menu_save_restore(int save) {
 		} 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) {
@@ -681,7 +685,9 @@ void global_game_menu() {
 				global_menu_save_restore(false);
 			} else {
 				kernel.activate_menu = GAME_NO_MENU;
-				g_engine->loadGameDialog();
+				if (g_engine->loadGameDialog())
+					// Dummy name to flag that load was successful
+					Common::strcpy_s(save_game_buf, "OK");
 			}
 			break;
 		case GAME_OPTIONS_MENU:


Commit: 82d619cab1d1545ee538fcaeaca5808a4babcd93
    https://github.com/scummvm/scummvm/commit/82d619cab1d1545ee538fcaeaca5808a4babcd93
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-19T19:50:56+10:00

Commit Message:
MADS: PHANTOM: Beginnings of textview namespace

Changed paths:
  A engines/mads/madsv2/textview/textview.cpp
  A engines/mads/madsv2/textview/textview.h
    engines/mads/madsv2/phantom/main.cpp
    engines/mads/madsv2/phantom/main_menu.cpp
    engines/mads/module.mk


diff --git a/engines/mads/madsv2/phantom/main.cpp b/engines/mads/madsv2/phantom/main.cpp
index 37fbaee4a48..f9c24c8db11 100644
--- a/engines/mads/madsv2/phantom/main.cpp
+++ b/engines/mads/madsv2/phantom/main.cpp
@@ -23,6 +23,7 @@
 #include "gui/saveload.h"
 #include "mads/madsv2/phantom/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"
@@ -305,12 +306,10 @@ void phantom_main() {
 			// Exit
 			return;
 
-		case 6:
-			// Quotes
-			break;
-
 		default:
 			// Credits
+			TextView::textview_main("credits");
+			selected_item = -1;
 			break;
 		}
 	}
diff --git a/engines/mads/madsv2/phantom/main_menu.cpp b/engines/mads/madsv2/phantom/main_menu.cpp
index f1fa3f3d918..727b1d769e5 100644
--- a/engines/mads/madsv2/phantom/main_menu.cpp
+++ b/engines/mads/madsv2/phantom/main_menu.cpp
@@ -167,15 +167,10 @@ void start_series() {
 		if (handle < 0) goto done;
 
 		menu_item[count].handle = handle;
-		menu_item[count].active = true;
+		menu_item[count].active = count != 6;	// Quotes button was never implemented
 		menu_item[count].status = 0;
 	}
 
-	if (menu_item[6].active) {
-		/* menu_item[6].active = config_file.quotes_enabled; */
-		menu_item[6].active = false;
-	}
-
 	error_flag = false;
 
 done:
diff --git a/engines/mads/madsv2/textview/textview.cpp b/engines/mads/madsv2/textview/textview.cpp
new file mode 100644
index 00000000000..5eb929c8896
--- /dev/null
+++ b/engines/mads/madsv2/textview/textview.cpp
@@ -0,0 +1,254 @@
+/* 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/textconsole.h"
+#include "mads/madsv2/core/cycle.h"
+#include "mads/madsv2/core/env.h"
+#include "mads/madsv2/core/font.h"
+#include "mads/madsv2/core/matte.h"
+#include "mads/madsv2/core/mcga.h"
+#include "mads/madsv2/core/mem.h"
+#include "mads/madsv2/core/mouse.h"
+#include "mads/madsv2/core/pal.h"
+#include "mads/madsv2/core/room.h"
+#include "mads/madsv2/core/timer.h"
+#include "mads/madsv2/textview/textview.h"
+#include "mads/madsv2/engine.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace TextView {
+
+constexpr int LINES_COUNT = 20;
+struct TextViewLine {
+	bool active;
+	int16 x, y;
+	char text[80];
+};
+
+Common::SeekableReadStream *file_handle;
+static Room *room;
+static TextViewLine lines[LINES_COUNT];
+static bool isGoing, active, flag2, flag3, isEnd;
+static bool flag5;
+static long timer1, timer2, curr_time;
+static char line_buffer[80];
+static bool has_background, pan_flag;
+static int peel_time, peel_x, peel_y;
+static int text_x, text_y;
+
+static void strip_linefeed(char *line) {
+	char *lf = strrchr(line, '\n');
+	if (lf)
+		*lf = '\0';
+}
+
+static void trim_right(char *line) {
+	Common::String s(line);
+	while (!s.empty() && (s.lastChar() == ' ' || s.lastChar() == '\t'))
+		s.deleteLastChar();
+
+	Common::strcpy_s(line, 80, s.c_str());
+}
+
+static void handle_command() {
+
+}
+
+static void position_line() {
+
+}
+
+static void shift_line(int lineNum) {
+
+}
+
+static void process_line() {
+	while (*line_buffer == '[')
+		handle_command();
+
+	if (flag5)
+		position_line();
+	else
+		flag2 = true;
+
+	if (active) {
+		active = false;
+		flag5 = true;
+	}
+}
+
+static void peel() {
+	if (peel_x) {
+		buffer_peel_horiz(&scr_orig, peel_x);
+		matte_refresh_work();
+	}
+
+	if (peel_y) {
+		buffer_peel_vert(&scr_orig, peel_y, scr_work.data, scr_work.x * scr_work.y);
+		matte_refresh_work();
+	}
+}
+
+static void animate() {
+	do {
+		if (g_engine->hasPendingKey()) {
+			g_engine->flushKeys();
+			isGoing = false;
+			break;
+		}
+
+		do {
+			if (flag3)
+				continue;
+
+			if (flag2 && file_handle->eos() && !isEnd) {
+				Common::String s = file_handle->readLine();
+				Common::strcpy_s(line_buffer, s.c_str());
+				strip_linefeed(line_buffer);
+				trim_right(line_buffer);
+				flag2 = false;
+
+				isEnd = file_handle->eos();
+				if (!strncmp(line_buffer, "***", 3)) {
+					// End of text
+					isEnd = flag2 = true;
+				}
+			}
+
+			if (!flag2)
+				process_line();
+
+		} while (flag2 && !flag3 && !isEnd);
+
+		curr_time = timer_read();
+
+		if (pan_flag && !has_background && curr_time >= timer1 && curr_time <= timer2) {
+			peel();
+			timer1 = curr_time + peel_time;
+			curr_time = timer_read();
+
+			if (curr_time < timer2)
+				matte_frame(0, 0);
+		}
+
+		if (curr_time >= timer2) {
+			int total = 0;
+			text_y = 0;
+
+			for (auto &msg : message_list) {
+				if (msg.active)
+					msg.status = -1;
+			}
+			for (int index = 0; index < LINES_COUNT; ++index) {
+				auto &line = lines[index];
+				if (line.active) {
+					++total;
+					shift_line(index);
+					text_y = MAX<int>(text_y, line.y);
+				}
+			}
+
+			if (isEnd && total == 0)
+				isGoing = false;
+
+			matte_frame(has_background ? 2 : 0, 0);
+			flag3 = has_background = false;
+			timer2 = curr_time + 6;
+		}
+	} while (isGoing && !g_engine->shouldQuit());
+}
+
+void textview_main(const char *resName) {
+	active = false;
+	isGoing = flag2 = true;
+	isEnd = flag3 = false;
+	timer1 = timer2 = 0;
+	has_background = pan_flag = false;
+	flag5 = 0;
+	text_x = text_y = 0;
+
+	file_handle = env_open(resName);
+	if (!file_handle)
+		error("textview -- failed to open response file : %s", resName);
+
+	font_conv = font_load("*fontconv.ff");
+	if (!font_conv)
+		error("textview -- failed to open font.");
+
+	mcga_compute_retrace_parameters();
+	memset(cycling_palette, 0, sizeof(cycling_palette));
+	cycle_list.num_cycles = 0;
+
+	// Initialize surfaces
+	pal_init(8, 8);
+	pal_white(master_palette);
+	buffer_init(&scr_work, 320, 156);
+	buffer_init(&scr_depth, 320, 156);
+	buffer_init(&scr_orig, 320, 156);
+	assert(scr_work.data && scr_depth.data && scr_orig.data);
+
+	viewing_at_y = (200 - scr_work.y) / 2;
+	room = nullptr;
+
+	// Clear buffers and palette
+	buffer_fill(scr_work, 0);
+	buffer_fill(scr_orig, 0);
+	buffer_fill(scr_depth, 0xff);
+	mcga_setpal(&master_palette);
+
+	mouse_set_work_buffer(scr_work.data, scr_work.x);
+	mouse_set_view_port_loc(0, viewing_at_y, scr_work.x, scr_work.y + viewing_at_y - 1);
+	mouse_set_view_port(0, 0);
+	timer_install();
+	matte_init(-1);
+	timer_activate_low_priority(cycle_colors);
+
+	// Draw boundary horizontal lines at top and bottom of the screen
+	auto &screen = *g_engine->getScreen();
+	if (viewing_at_y) {
+		screen.hLine(0, viewing_at_y - 2, 319, 2);
+		screen.hLine(0, viewing_at_y + scr_work.y + 1, 319, 2);
+	}
+
+	// Clear the lines
+	memset(lines, 0, sizeof(lines));
+
+	// Handle the loading and animation of the text lines
+	animate();
+
+	// Teardown
+	g_engine->_soundManager->removeDriver();
+	pal_interface(master_palette);
+	mcga_setpal(&master_palette);
+	mcga_reset();
+
+	buffer_free(&scr_depth);
+	buffer_free(&scr_orig);
+	buffer_free(&scr_work);
+	mem_free(font_conv);
+
+	delete file_handle;
+}
+
+} // namespace TextView
+} // namespace MADSV2
+} // namespace MADS
diff --git a/engines/mads/madsv2/textview/textview.h b/engines/mads/madsv2/textview/textview.h
new file mode 100644
index 00000000000..c9269a78870
--- /dev/null
+++ b/engines/mads/madsv2/textview/textview.h
@@ -0,0 +1,38 @@
+/* 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 MADSV2_TEXTVIEW_H
+#define MADSV2_TEXTVIEW_H
+
+#include "mads/madsv2/core/general.h"
+
+namespace MADS {
+namespace MADSV2 {
+namespace TextView {
+
+// Main textview function
+extern void textview_main(const char *resName);
+
+} // namespace TextView
+} // namespace MADSV2
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/module.mk b/engines/mads/module.mk
index effd6a42ca0..65e1e8a416b 100644
--- a/engines/mads/module.mk
+++ b/engines/mads/module.mk
@@ -60,6 +60,7 @@ MODULE_OBJS += \
 	madsv2/animview/anim_timer.o \
 	madsv2/animview/animview.o \
 	madsv2/animview/functions.o \
+	madsv2/textview/textview.o \
 	madsv2/core/anim.o \
 	madsv2/core/asound.o \
 	madsv2/core/attr.o \


Commit: 3d306a1e386cf8685b2cdbdd3939ee518aa46847
    https://github.com/scummvm/scummvm/commit/3d306a1e386cf8685b2cdbdd3939ee518aa46847
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2026-05-19T19:50:57+10:00

Commit Message:
MADS: PHANTOM: Further textview implementation

Changed paths:
    engines/mads/madsv2/textview/textview.cpp


diff --git a/engines/mads/madsv2/textview/textview.cpp b/engines/mads/madsv2/textview/textview.cpp
index 5eb929c8896..2b9ff423da8 100644
--- a/engines/mads/madsv2/textview/textview.cpp
+++ b/engines/mads/madsv2/textview/textview.cpp
@@ -39,7 +39,7 @@ namespace TextView {
 
 constexpr int LINES_COUNT = 20;
 struct TextViewLine {
-	bool active;
+	int16 active;
 	int16 x, y;
 	char text[80];
 };
@@ -47,13 +47,24 @@ struct TextViewLine {
 Common::SeekableReadStream *file_handle;
 static Room *room;
 static TextViewLine lines[LINES_COUNT];
-static bool isGoing, active, flag2, flag3, isEnd;
-static bool flag5;
-static long timer1, timer2, curr_time;
+static bool isGoing, active, flag2, flag3;
+static bool flag4, flag5, isEnd;
+static int16 font_auto_spacing;
+static long timer1, timer2, timer3, curr_time;
 static char line_buffer[80];
+static char command_buffer[80];
 static bool has_background, pan_flag;
-static int peel_time, peel_x, peel_y;
-static int text_x, text_y;
+static int16 peel_time, peel_x, peel_y;
+static int16 text_x, text_y;
+static int16 room_id;
+static bool spare[3];
+static Buffer room_picture[3];
+static Buffer *background_ptr;
+static int16 xPos;
+
+static void textview_timer() {
+	// TODO
+}
 
 static void strip_linefeed(char *line) {
 	char *lf = strrchr(line, '\n');
@@ -69,16 +80,230 @@ static void trim_right(char *line) {
 	Common::strcpy_s(line, 80, s.c_str());
 }
 
-static void handle_command() {
+static bool check_command(const char *cmd, const char **value = nullptr) {
+	size_t len = strlen(cmd);
+	if (value)
+		*value = command_buffer + len;
+
+	return strncmp(command_buffer, cmd, len) == 0;
+}
+
+static void find_number(const char *&value) {
+	while (*value && !Common::isDigit(*value))
+		++value;
+}
+
+static void find_next_number(const char *&value) {
+	if (*value) {
+		while (*value && *value != ',')
+			++value;
+	}
+	if (*value) {
+		while (*value && !Common::isDigit(*value))
+			++value;
+	}
+}
+
+static void load_background(const char *value) {
+	find_number(value);
+	room_id = atoi(value);
+	has_background = true;
+	flag5 = false;
+	active = false;
+
+	pal_init(8, 8);
+	pal_white(master_palette);
+	master_palette[5].r = 0;
+	master_palette[5].g = 63;
+	master_palette[5].b = 63;
+	master_palette[6].r = 0;
+	master_palette[6].g = 45;
+	master_palette[6].b = 45;
+
+	room = room_load(room_id, 0, nullptr, &scr_orig, &scr_depth, &scr_walk,
+		&scr_special, &picture_map, &depth_map, &picture_resource,
+		&depth_resource, -1, -1, 0);
+	if (!room)
+		error("Could not load room %d", room_id);
+
+	image_marker = 1;
+	image_list[0].flags = IMAGE_REFRESH;
+	image_list[0].segment_id = 0xff;
+}
+
+static void pan(const char *value) {
+	find_number(value);
+	peel_x = atoi(value);
+	find_next_number(value);
+	peel_y = atoi(value);
+	find_next_number(value);
+	peel_time = atoi(value);
+
+	pan_flag = peel_x != 0 || peel_y != 0;
+}
+
+static void sound_driver(const char *value) {
+	g_engine->_soundManager->removeDriver();
+
+	char num = value[strlen(value) - 1];
+	if (Common::isDigit(num))
+		g_engine->_soundManager->init(num - '0');
+}
+
+static void play_sound(const char *value) {
+	find_number(value);
+	int num = atoi(value);
+
+	if (g_engine->_soundManager->isLoaded())
+		g_engine->_soundManager->command(num);
+}
+
+static void set_color(int index, const char *value) {
+	find_number(value);
+	master_palette[5 + index].r = atoi(value);
+	find_next_number(value);
+	master_palette[5 + index].g = atoi(value);
+	find_next_number(value);
+	master_palette[5 + index].b = atoi(value);
+
+	mcga_setpal_range(&master_palette, 5 + index, 1);
+}
+
+static void set_spare(const char *value) {
+	find_number(value);
+	int index = atoi(value);
+	find_next_number(value);
+	int roomId = atoi(value);
+
+	if (spare[index]) {
+		buffer_free(&room_picture[index]);
+		spare[index] = false;
+	}
 
+	spare[index] = room_picture_load(roomId, &room_picture[index], 0) == 0;
+}
+
+static void set_page(const char *value) {
+	find_number(value);
+	int index = atoi(value);
+
+	if (spare[index] && !flag4) {
+		background_ptr = &room_picture[index];
+		flag4 = true;
+		xPos = 0;
+		timer3 = timer_read();
+		timer_activate_low_priority(textview_timer);
+	}
+}
+
+static void handle_command() {
+	const char *value = nullptr;
+
+	// Get end of command
+	*command_buffer = '\0';
+	char *end = strchr(line_buffer, ']');
+	if (!end)
+		end = line_buffer + strlen(line_buffer);
+	char endChar = *end;
+	*end = '\0';
+
+	// Copy out the command
+	Common::strcpy_s(command_buffer, line_buffer);
+	mads_strupr(command_buffer);
+
+	*end = endChar;
+	if (endChar)
+		Common::strcpy_s(line_buffer, end + 1);
+
+	if (check_command("BACKGROUND", &value))
+		load_background(value);
+	if (check_command("GO"))
+		active = true;
+	if (check_command("PAN", &value))
+		pan(value);
+	if (check_command("DRIVER", &value))
+		sound_driver(value);
+	if (check_command("SOUND", &value))
+		play_sound(value);
+	if (check_command("COLOR0", &value))
+		set_color(0, value);
+	if (check_command("COLOR1", &value))
+		set_color(1, value);
+	if (check_command("SPARE", &value))
+		set_spare(value);
+	if (check_command("PAGE", &value))
+		set_page(value);
 }
 
 static void position_line() {
+	char tmp_buf[84];
+
+	if ((int)font_conv->max_y_size + text_y + 2 >= 156)
+		return;
+
+	// Split line_buffer at first '[' (start of commands) or at the null terminator
+	char *split = strchr(line_buffer, '[');
+	if (!split)
+		split = line_buffer + strlen(line_buffer);
+
+	// Copy text portion (before split) to tmp_buf, then shift remainder to line_buffer start
+	char saved = *split;
+	*split = '\0';
+	Common::strcpy_s(tmp_buf, line_buffer);
+	*split = saved;
+	Common::strcpy_s(line_buffer, split);
+
+	if (!strlen(line_buffer))
+		flag2 = flag3 = true;
+
+	// Handle '@' horizontal-position marker
+	char *at = strchr(tmp_buf, '@');
+	int width;
+	if (at) {
+		*at = '\0';
+		int left_width = font_string_width(font_conv, tmp_buf, font_auto_spacing);
+		memmove(at, at + 1, strlen(at + 1) + 1);
+		if (text_x == -1)
+			text_x = 160 - left_width;
+		width = font_string_width(font_conv, tmp_buf, font_auto_spacing);
+	} else {
+		width = font_string_width(font_conv, tmp_buf, font_auto_spacing);
+		if (text_x == -1)
+			text_x = 160 - width / 2;
+	}
+
+	// Find first inactive slot in lines[]
+	int slot = -1;
+	for (int i = 0; i < LINES_COUNT; ++i) {
+		if (!lines[i].active) {
+			slot = i;
+			break;
+		}
+	}
 
+	if (slot >= 0) {
+		if (text_x < 0 || text_x + width - 1 >= 320)
+			warning("position_line: text out of bounds: text_x=%d width=%d", text_x, width);
+
+		lines[slot].active = -1;
+		lines[slot].x = text_x;
+		lines[slot].y = 155;
+		Common::strcpy_s(lines[slot].text, tmp_buf);
+	}
+
+	text_x = -1;
 }
 
 static void shift_line(int lineNum) {
-
+	int fontH = font_conv->max_y_size;
+	TextViewLine &line = lines[lineNum];
+
+	if ((line.y + fontH) < 0) {
+		line.active = false;
+	} else {
+		matte_add_message(font_conv, line.text, line.x, line.y, 0x605, font_auto_spacing);
+		line.y--;
+	}
 }
 
 static void process_line() {
@@ -174,6 +399,8 @@ static void animate() {
 			flag3 = has_background = false;
 			timer2 = curr_time + 6;
 		}
+
+		g_system->delayMillis(10);
 	} while (isGoing && !g_engine->shouldQuit());
 }
 
@@ -181,10 +408,17 @@ void textview_main(const char *resName) {
 	active = false;
 	isGoing = flag2 = true;
 	isEnd = flag3 = false;
-	timer1 = timer2 = 0;
+	flag4 = false;
+	timer1 = timer2 = timer3 = 0;
 	has_background = pan_flag = false;
 	flag5 = 0;
 	text_x = text_y = 0;
+	font_auto_spacing = -1;
+	room_id = 0;
+	memset(spare, 0, sizeof(spare));
+	memset(&room_picture, 0, sizeof(room_picture));
+	background_ptr = nullptr;
+	xPos = 0;
 
 	file_handle = env_open(resName);
 	if (!file_handle)




More information about the Scummvm-git-logs mailing list