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