--- 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);