src/demo.cpp

changeset 37
c82a86ea87be
parent 36
b8fa9171be6e
child 38
db677d321cf4
--- a/src/demo.cpp	Mon Jun 01 17:06:13 2015 +0300
+++ b/src/demo.cpp	Fri Jun 05 18:33:51 2015 +0300
@@ -1,6 +1,6 @@
 /*
  *  ZCinema: Zandronum demo launcher
- *  Copyright (C) 2013 Santeri Piippo
+ *  Copyright (C) 2013-2015 Teemu Piippo
  *
  *  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
@@ -25,23 +25,23 @@
 #include "ui_demoprompt.h"
 #include "prompts.h"
 
-EXTERN_CONFIG (Map,  binaryPaths)
-EXTERN_CONFIG (List, wadpaths)
-EXTERN_CONFIG (Bool, noprompt)
+//
+// -------------------------------------------------------------------------------------------------
+//
 
-static const uint32 g_demoSignature = makeByteID ('Z', 'C', 'L', 'D');
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-str uncolorize (const str& in) {
-	str out;
+QString uncolorize (const QString& in)
+{
+	// TODO: Handle long-form colors like \c[Red]
+	QString out;
 	int skip = 0;
 	
-	for (const QChar& c : in) {
+	for (QChar c : in)
+	{
 		if (skip-- > 0)
 			continue;
 		
-		if (c == QChar ('\034')) {
+		if (c == QChar ('\034'))
+		{
 			skip = 1;
 			continue;
 		}
@@ -52,41 +52,59 @@
 	return out;
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-static str tr (const char* msg) {
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+static QString tr (const char* msg)
+{
 	return QObject::tr (msg);
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-static void error (str msg) {
-	QMessageBox::critical (null, "Error", msg);
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+static void error (QString msg)
+{
+	QMessageBox::critical (NULL, "Error", msg);
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-static bool isKnownVersion (str ver) {
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+static bool isKnownVersion (QString ver)
+{
 	for (const QVariant& it : getVersions())
+	{
 		if (it.toString() == ver || it.toString() + 'M' == ver)
 			return true;
+	}
 	
 	return false;
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-static str findWAD (str name) {
-	if (cfg::wadpaths.size() == 0) {
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+static QString findWAD (QString name)
+{
+	QStringList wadpaths = Config::get ("wadpaths").toStringList();
+
+	if (wadpaths.empty())
+	{
 		error (tr ("No WAD paths configured!"));
-		
+
 		// Cannot just return an empty string here since that'd trigger
 		// another error prompt - skip ahead and exit.
 		exit (9);
 	}
-	
-	for (const QVariant& it : cfg::wadpaths) {
-		str fullpath = fmt ("%1/%2", it.toString(), name);
+
+	for (int i = 0; i < wadpaths.size(); ++i)
+	{
+		QString fullpath = QString ("%1/%2").arg (wadpaths[i]).arg (name);
 		QFile f (fullpath);
 		
 		if (f.exists())
@@ -96,54 +114,78 @@
 	return "";
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-str readString (QDataStream& stream) {
-	str out;
-	uint8 c;
-	
-	for (stream >> c; c != '\0'; stream >> c)
-		out += c;
-	
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+QString readString (QDataStream& stream)
+{
+	QString out;
+	uint8 ch;
+
+	for (stream >> ch; ch != 0; stream >> ch)
+		out += QChar (ch);
+
 	return out;
 }
 
-// =============================================================================
-// -----------------------------------------------------------------------------
-int launchDemo (str path) {
+//
+// -------------------------------------------------------------------------------------------------
+//
+
+struct UserInfo
+{
+	QString netname;
+	QString skin;
+	QString className;
+	uint32 color;
+	uint32 aimdist;
+	uint32 railcolor;
+	uint8 gender;
+	uint8 handicap;
+	uint8 unlagged;
+	uint8 respawnOnFire;
+	uint8 ticsPerUpdate;
+	uint8 connectionType;
+};
+
+int launchDemo (QString path)
+{
 	QFile f (path);
 	
-	if (!f.open (QIODevice::ReadOnly)) {
-		error (fmt (tr ("Couldn't open '%1' for reading: %2"), path, strerror (errno)));
+	if (not f.open (QIODevice::ReadOnly))
+	{
+		error (tr ("Couldn't open '%1' for reading: %2").arg (path).arg (strerror (errno)));
 		return 1;
 	}
 	
 	QDataStream stream (&f);
 	stream.setByteOrder (QDataStream::LittleEndian);
-	
+
 	uint8 offset;
 	uint32 length;
 	uint16 zanversionID, numWads;
 	uint32 longSink;
-	str zanversion;
-	list<str> wads;
-	BuildType buildID;
+	QString zanversion;
+	QStringList wads;
+	UserInfo userinfo;
+
+	// Assume a release build if the build ID not supplied. The demo only got the "ZCLD" signature
+	// in the 1.1 release build, 1.1.1 had no testing binaries and the build ID is included in 1.2
+	// onward.
+	BuildType buildID = ReleaseBuild;
+
 	bool ready = false;
-	
-	struct {
-		str netname, skin, className;
-		uint8 gender, handicap, unlagged, respawnOnFire, ticsPerUpdate, connectionType;
-		uint32 color, aimdist, railcolor;
-	} userinfo;
-	
+
 	// Check signature
 	{
-		uint32 sig;
-		
-		stream >> sig;
-		if (sig != g_demoSignature) {
-			error (fmt (tr ("'%1' is not a Zandronum demo file!"), path));
-			return 3;
+		uint32 demosignature;
+		stream >> demosignature;
+
+		if (demosignature != makeByteID ('Z', 'C', 'L', 'D'))
+		{
+			error (tr ("'%1' is not a valid Zandronum demo file!").arg (path));
+			return 1;
 		}
 	}
 	
@@ -154,48 +196,52 @@
 	       >> length;
 	
 	// Read the demo header and get data
-	for (;;) {
+	for (;;)
+	{
 		uint8 header;
 		stream >> header;
 		
-		if (header == DemoBodyStart + offset) {
+		if (header == DemoBodyStart + offset)
+		{
 			ready = true;
 			break;
-		} elif (header == DemoVersion + offset) {
+		}
+		else if (header == DemoVersion + offset)
+		{
 			stream >> zanversionID;
 			zanversion = readString (stream);
-			
-			if (!zanversion.startsWith ("1.1-") && !zanversion.startsWith ("1.1.1-")) {
+
+			if (not zanversion.startsWith ("1.1-") and not zanversion.startsWith ("1.1.1-"))
+			{
 				uint8 a;
 				stream >> a;
 				buildID = (BuildType) a;
-			} else {
-				// Assume a release build if not supplied. The demo only got the
-				// "ZCLD" signature in the 1.1 release build, 1.1.1 had no testing
-				// binaries and the build ID is included in 1.2 onward.
-				buildID = ReleaseBuild;
 			}
-			
+
 			stream >> longSink; // rng seed - we don't need it
-		} elif (header == DemoUserInfo + offset) {
+		}
+		else if (header == DemoUserInfo + offset)
+		{
 			userinfo.netname = readString (stream);
-			stream >> userinfo.gender
-			       >> userinfo.color
-			       >> userinfo.aimdist;
+			stream >> userinfo.gender;
+			stream >> userinfo.color;
+			stream >> userinfo.aimdist;
 			userinfo.skin = readString (stream);
-			stream >> userinfo.railcolor
-			       >> userinfo.handicap
-			       >> userinfo.unlagged
-			       >> userinfo.respawnOnFire
-			       >> userinfo.ticsPerUpdate
-			       >> userinfo.connectionType;
+			stream >> userinfo.railcolor;
+			stream >> userinfo.handicap;
+			stream >> userinfo.unlagged;
+			stream >> userinfo.respawnOnFire;
+			stream >> userinfo.ticsPerUpdate;
+			stream >> userinfo.connectionType;
 			userinfo.className = readString (stream);
-		} elif (header == DemoWads + offset) {
-			str sink;
+		}
+		else if (header == DemoWads + offset)
+		{
+			QString sink;
 			stream >> numWads;
 			
 			for (uint8 i = 0; i < numWads; ++i) {
-				str wad = readString (stream);
+				QString wad = readString (stream);
 				wads << wad;
 			}
 			
@@ -203,70 +249,74 @@
 			// in them though. Down the sink they go...
 			for (int i = 0; i < 2; ++i)
 				sink = readString (stream);
-		} else {
-			error (fmt (tr ("Unknown header %1!\n"), (int) header));
-			return 3;
+		}
+		else
+		{
+			error (tr ("Unknown header %1!\n").arg (int (header)));
+			return 1;
 		}
 	}
 
-	if (!ready) {
-		error (fmt (tr ("Incomplete demo header in '%s'!"), path));
-		return 3;
-	}
-	
-	if (!isKnownVersion (zanversion)) {
-		UnknownVersionPrompt* prompt = new UnknownVersionPrompt (path, zanversion,
-			(buildID == ReleaseBuild));
-		
-		if (!prompt->exec())
-			return 6;
-		
-		if (!isKnownVersion (zanversion)) {
-			error (tr ("Failure in configuration! This shouldn't happen."));
-			return 6;
-		}
+	if (not ready)
+	{
+		error (tr ("Incomplete demo header in '%s'!").arg (path));
+		return 1;
 	}
 	
-	str binarypath = cfg::binaryPaths [zanversion].toString();
-	
-	if (binarypath.isEmpty()) {
-		error (fmt (tr ("No binary path specified for Zandronum version %1!"), zanversion));
-		return 7;
+	if (not isKnownVersion (zanversion))
+	{
+		QDialog* prompt = new UnknownVersionPrompt (path, zanversion, (buildID == ReleaseBuild));
+
+		if (not prompt->exec())
+			return 1;
+	}
+
+	QString binarypath = Config::get ("binarypaths").toMap()[zanversion].toString();
+
+	if (binarypath.isEmpty())
+	{
+		error (tr ("No binary path specified for Zandronum version %1!").arg (zanversion));
+		return 1;
 	}
 	
-	str iwadpath;
-	list<str> pwadpaths;
+	QString iwadpath;
+	QStringList pwadpaths;
 	
 	// Find the WADs
-	for (const str& wad : wads) {
-		str path = findWAD (wad);
-		
-		// IWAD names can appear in uppercase too. Linux is case-sensitive, so..
-		if (&wad == &wads[0] && path.isEmpty())
+	for (const QString& wad : wads)
+	{
+		QString path = findWAD (wad);
+
+		// WAD names are case-sensitive under non-Windows and they can appear in uppercase
+		// so we need to test that too.
+		if (path.isEmpty())
 			path = findWAD (wad.toUpper());
 		
-		if (path.isEmpty()) {
-			error (fmt (tr ("Couldn't find %1!"), wad));
-			return 8;
+		if (path.isEmpty())
+		{
+			error (tr ("Couldn't find %1!").arg (wad));
+			return 1;
 		}
-		
-		if (&wad == &wads[0])
+
+		if (&wad == &wads.first())
 			iwadpath = path;
 		else
 			pwadpaths << path;
 	}
 	
-	if (!cfg::noprompt) {
-		str pwadtext;
+	if (not Config::get ("noprompt").toBool())
+	{
+		QString pwadtext;
 		
-		for (const str& pwad : wads) {
-			if (&pwad == &wads[0])
+		for (const QString& wad : wads)
+		{
+			if (&wad == &wads.first())
 				continue; // skip the IWAD
 			
-			if (!pwadtext.isEmpty())
+			if (not pwadtext.isEmpty())
 				pwadtext += "<br />";
 			
-			pwadtext += pwad;
+			pwadtext += wad;
 		}
 		
 		QDialog* dlg = new QDialog;
@@ -279,19 +329,17 @@
 		ui.pwadsLabel->setText (pwadtext);
 		dlg->setWindowTitle (versionSignature());
 		
-		if (!dlg->exec())
-			return 1;
+		if (not dlg->exec())
+			return 0;
 	}
 
 	QStringList cmdlineList;
 	cmdlineList << "-playdemo" << path << "-iwad" << iwadpath;
-	
-	if (pwadpaths.size() > 0) {
-		cmdlineList << "-file";
-		cmdlineList << pwadpaths;
-	}
-	
-	print ("Executing: %1 %2\n", binarypath, cmdlineList.join (" "));
+
+	if (pwadpaths.size() > 0)
+		cmdlineList << "-file" << pwadpaths;
+
+	// print ("Executing: %1 %2\n", binarypath, cmdlineList.join (" "));
 	QProcess* proc = new QProcess;
 	proc->start (binarypath, cmdlineList);
 	proc->waitForFinished (-1);

mercurial