1 #include <unordered_map>
2 #include <memory>
3 #include <functional>
4 
5 #include <QApplication>
6 #include <QDir>
7 #include <QSplashScreen>
8 #include <QTimer>
9 #include <QtCore/QLoggingCategory>
10 
11 #ifdef _WIN32
12 #include "globalincs/mspdb_callstack.h"
13 #endif
14 
15 #include "mission/Editor.h"
16 #include "mission/management.h"
17 #include "ui/widgets/renderwidget.h"
18 #include "globalincs/pstypes.h"
19 
20 #include "ui/FredView.h"
21 #include "FredApplication.h"
22 
23 #include <csignal>
24 #include <project.h>
25 
26 // Globals needed by the engine when built in 'FRED' mode.
27 int Fred_running = 1;
28 int Show_cpu = 0;
29 
30 // Empty functions to make fred link with the sexp_mission_set_subspace
game_start_subspace_ambient_sound()31 void game_start_subspace_ambient_sound() {
32 }
game_stop_subspace_ambient_sound()33 void game_stop_subspace_ambient_sound() {
34 }
35 
fsoMessageOutput(QtMsgType type,const QMessageLogContext & context,const QString & msg)36 void fsoMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
37 {
38 	auto errorMsg = qFormatLogMessage(type, context, msg).toUtf8();
39 	switch (type) {
40 	case QtDebugMsg:
41 		mprintf(("Qt Debug: %s\n", errorMsg.constData()));
42 		fprintf(stderr, "Qt Debug: %s\n", errorMsg.constData());
43 		break;
44 	case QtInfoMsg:
45 		mprintf(("Qt Info: %s\n", errorMsg.constData()));
46 		fprintf(stderr, "Qt Info: %s\n", errorMsg.constData());
47 		break;
48 	case QtWarningMsg:
49 		mprintf(("Qt Warning: %s\n", errorMsg.constData()));
50 		fprintf(stderr, "Qt Warning: %s\n", errorMsg.constData());
51 		break;
52 	case QtCriticalMsg:
53 		mprintf(("Qt Critical: %s\n", errorMsg.constData()));
54 		fprintf(stderr, "Qt Critical: %s\n", errorMsg.constData());
55 		break;
56 	case QtFatalMsg:
57 		Error(context.file == nullptr ? "Unknown" : context.file, context.line, "Qt Critical: %s", errorMsg.constData());
58 		break;
59 	}
60 }
61 
62 // SDL defines this on windows which causes problems
63 #ifdef main
64 #undef main
65 #endif
66 
handler(int signal)67 void handler(int signal) {
68 	auto stacktrace = dump_stacktrace();
69 
70 	fprintf(stderr, "Stack: %s\n", stacktrace.c_str());
71 	exit( signal );
72 }
73 
74 // Our callback to keep the window responsive while loading
game_busy_callback(int)75 void game_busy_callback(int  /*count*/) {
76 	qGuiApp->processEvents();
77 }
78 
main(int argc,char * argv[])79 int main(int argc, char* argv[]) {
80 	signal( SIGSEGV, handler );
81 
82 	using namespace fso::fred;
83 
84 #ifdef WIN32
85 	SCP_mspdbcs_Initialise();
86 #endif
87 
88 	if (LoggingEnabled) {
89 		outwnd_init();
90 	}
91 
92 	qInstallMessageHandler(fsoMessageOutput);
93 
94 	SDL_SetMainReady();
95 
96 	QCoreApplication::setOrganizationName("HardLightProductions");
97 	QCoreApplication::setOrganizationDomain("hard-light.net");
98 	QCoreApplication::setApplicationName("qtFRED");
99 
100 	QApplication app(argc, argv);
101 
102 	// Expect that the platform library is in the same directory
103 	QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath());
104 
105 	QGuiApplication::setApplicationDisplayName(app.tr("qtFRED v%1").arg(FS_VERSION_FULL));
106 
107 #ifndef NDEBUG
108 	QLoggingCategory::defaultCategory()->setEnabled(QtDebugMsg, true);
109 	QLoggingCategory::defaultCategory()->setEnabled(QtInfoMsg, true);
110 #endif
111 
112 	QGuiApplication::setWindowIcon(QIcon(":/images/V_fred.ico"));
113 
114 	// This will be available as the global instance
115 	FredApplication localFredApp;
116 
117 	QSplashScreen splash(QPixmap(":/images/splash.png"));
118 	splash.show();
119 	qGuiApp->processEvents();
120 	std::unique_ptr<Editor> fred(new Editor());
121 
122 	auto baseDir = QDir::toNativeSeparators(QDir::current().absolutePath());
123 
124 	typedef std::unordered_map<SubSystem, QString> SubsystemMap;
125 	SubsystemMap initializers = {{ SubSystem::OS,                app.tr("Initializing OS interface") },
126 								 { SubSystem::CommandLine,       app.tr("Parsing command line") },
127 								 { SubSystem::Timer,             app.tr("Initializing Timer") },
128 								 { SubSystem::CFile,             app.tr("Initializing CFile") },
129 								 { SubSystem::Locale,            app.tr("Initializing locale") },
130 								 { SubSystem::Sound,             app.tr("Initializing sound") },
131 								 { SubSystem::Graphics,          app.tr("Initializing graphics") },
132 								 { SubSystem::Scripting,         app.tr("Initializing scripting") },
133 								 { SubSystem::Fonts,             app.tr("Initializing Fonts") },
134 								 { SubSystem::Keyboard,          app.tr("Initializing keyboard") },
135 								 { SubSystem::Mouse,             app.tr("Initializing mouse") },
136 								 { SubSystem::Particles,         app.tr("Initializing particles") },
137 								 { SubSystem::Iff,               app.tr("Initializing IFF") },
138 								 { SubSystem::Objects,           app.tr("Initializing objects") },
139 								 { SubSystem::Models,            app.tr("Initializing model system") },
140 								 { SubSystem::Species,           app.tr("Initializing species") },
141 								 { SubSystem::BriefingIcons,     app.tr("Initializing briefing icons") },
142 								 { SubSystem::HudCommOrders,     app.tr("Initializing HUD comm orders") },
143 								 { SubSystem::AlphaColors,       app.tr("Initializing alpha colors") },
144 								 { SubSystem::GameSound,         app.tr("Initializing briefing icons") },
145 								 { SubSystem::MissionBrief,      app.tr("Initializing briefings") },
146 								 { SubSystem::AI,                app.tr("Initializing AI") },
147 								 { SubSystem::AIProfiles,        app.tr("Initializing AI profiles") },
148 								 { SubSystem::Armor,             app.tr("Initializing armors") },
149 								 { SubSystem::Weapon,            app.tr("Initializing weaponry") },
150 								 { SubSystem::Medals,            app.tr("Initializing medals") },
151 								 { SubSystem::Glowpoints,        app.tr("Initializing glow points") },
152 								 { SubSystem::Ships,             app.tr("Initializing ships") },
153 								 { SubSystem::Parse,             app.tr("Initializing parser") },
154 								 { SubSystem::TechroomIntel,     app.tr("Initializing techroom intel") },
155 								 { SubSystem::Nebulas,           app.tr("Initializing nebulas") },
156 								 { SubSystem::Stars,             app.tr("Initializing stars") },
157 								 { SubSystem::Ssm,               app.tr("Initializing SSMs") },
158 								 { SubSystem::EventMusic,        app.tr("Initializing event music") },
159 								 { SubSystem::FictionViewer,     app.tr("Initializing fiction viewer") },
160 								 { SubSystem::CommandBriefing,   app.tr("Initializing command briefing") },
161 								 { SubSystem::Campaign,          app.tr("Initializing campaign system") },
162 								 { SubSystem::NebulaLightning,   app.tr("Initializing nebula lightning") },
163 								 { SubSystem::FFmpeg,            app.tr("Initializing FFmpeg") },
164 								 { SubSystem::DynamicSEXPs,      app.tr("Initializing dynamic SEXP system") },
165 								 { SubSystem::ScriptingInitHook, app.tr("Running game init scripting hook") },
166 	};
167 
168 	auto initSuccess = fso::fred::initialize(baseDir.toStdString(), argc, argv, fred.get(), [&](const SubSystem& which) {
169 		if (initializers.count(which)) {
170 			splash.showMessage(initializers.at(which), Qt::AlignHCenter | Qt::AlignBottom, Qt::white);
171 		}
172 		qGuiApp->processEvents();
173 	});
174 
175 	if (!initSuccess) {
176 		return -1;
177 	}
178 
179 	splash.showMessage(app.tr("Showing editor window"), Qt::AlignHCenter | Qt::AlignBottom, Qt::white);
180 	splash.finish(qApp->activeWindow());
181 
182 	// Use this to keep the app responsive
183 	game_busy_callback(game_busy_callback);
184 
185 	// Find and show our window from the top level windows
186 	FredView* fredview(nullptr);
187 	for (auto& window : qApp->topLevelWidgets()) {
188 		fredview = qobject_cast<FredView*>(window);
189 		if (fredview != nullptr) break;
190 	}
191 	Assert(fredview != nullptr);
192 	fredview->showMaximized();
193 
194 	// Allow other parts of the code to execute code that needs to run after everything has been set up
195 	fredApp->initializeComplete();
196 
197 	if (Cmdline_start_mission) {
198 		// Automatically load a mission if specified on the command line
199 		QTimer::singleShot(500, [&]() {
200 			fred->loadMission(Cmdline_start_mission);
201 		});
202 	}
203 
204 	// Render first correct frame
205 	QTimer::singleShot(50, [=]{
206 		Assert(fredview != nullptr);
207 		fredview->getRenderWidget()->renderFrame();
208 	});
209 
210 	return QGuiApplication::exec();
211 }
212