Overhauled the way versions are handled, it's all kept dynamically now.

Sun, 11 Aug 2013 02:58:55 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sun, 11 Aug 2013 02:58:55 +0300
changeset 10
bc1414343e19
parent 9
f9893eea978b
child 11
3ddebf76105e

Overhauled the way versions are handled, it's all kept dynamically now.

src/bytestream.cpp file | annotate | diff | comparison | revisions
src/config.cpp file | annotate | diff | comparison | revisions
src/config.h file | annotate | diff | comparison | revisions
src/demo.cpp file | annotate | diff | comparison | revisions
src/demo.h file | annotate | diff | comparison | revisions
src/main.cpp file | annotate | diff | comparison | revisions
src/main.h file | annotate | diff | comparison | revisions
src/misc.cpp file | annotate | diff | comparison | revisions
src/misc.h file | annotate | diff | comparison | revisions
src/src.pro file | annotate | diff | comparison | revisions
src/ui/configbox.ui file | annotate | diff | comparison | revisions
--- a/src/bytestream.cpp	Wed Jul 17 19:45:19 2013 +0300
+++ b/src/bytestream.cpp	Sun Aug 11 02:58:55 2013 +0300
@@ -7,38 +7,38 @@
 	float f;
 } g_floatunion;
 
-Bytestream::Bytestream( ulong len ) {
+Bytestream::Bytestream (ulong len) {
 	m_data = null;
-	resize( len );
+	resize (len);
 	clear();
 }
 
-Bytestream::Bytestream( const char* data, ulong len ) {
+Bytestream::Bytestream (const char* data, ulong len) {
 	m_data = null;
-	init( data, len );
+	init (data, len);
 }
 
-void Bytestream::resize( ulong newsize ) {
+void Bytestream::resize (ulong newsize) {
 	char* olddata = null;
 	ulong oldsize;
-	
-	if( m_data ) {
+
+	if (m_data) {
 		oldsize = m_size;
 		olddata = new char[oldsize];
-		memcpy( olddata, m_data, oldsize );
+		memcpy (olddata, m_data, oldsize);
 	}
-	
+
 	delete[] m_data;
 	m_data = new uint8[newsize];
 	m_size = newsize;
-	
-	if( olddata )
-		memcpy( m_data, olddata, min<ulong> ( oldsize, newsize ));
+
+	if (olddata)
+		memcpy (m_data, olddata, min<ulong> (oldsize, newsize));
 }
 
-void Bytestream::init( const char* data, ulong len ) {
-	resize( len );
-	memcpy( m_data, data, len );
+void Bytestream::init (const char* data, ulong len) {
+	resize (len);
+	memcpy (m_data, data, len);
 	m_ptr = &m_data[0];
 	m_len = len;
 }
@@ -52,15 +52,15 @@
 	m_len = 0;
 }
 
-uint8& Bytestream::subscript( ulong idx ) {
+uint8& Bytestream::subscript (ulong idx) {
 	return m_data[idx];
 }
 
-const uint8& Bytestream::const_subscript( ulong idx ) const {
+const uint8& Bytestream::const_subscript (ulong idx) const {
 	return m_data[idx];
 }
 
-void Bytestream::seek( ulong pos ) {
+void Bytestream::seek (ulong pos) {
 	m_ptr = m_data + pos;
 }
 
@@ -70,51 +70,51 @@
 }
 
 ulong Bytestream::bytesLeft() const {
-	return ( m_len - ( m_ptr - &m_data[0] ));
+	return (m_len - (m_ptr - &m_data[0]));
 }
 
 ulong Bytestream::spaceLeft() const {
-	return ( m_size - m_len );
+	return (m_size - m_len);
 }
 
 // =============================================================================
-bool Bytestream::readByte( uint8& val ) {
-	if( bytesLeft() < 1 )
+bool Bytestream::readByte (uint8& val) {
+	if (bytesLeft() < 1)
 		return false;
-	
+
 	val = *m_ptr++;
 	return true;
 }
 
 // =============================================================================
-bool Bytestream::readShort( uint16& val ) {
-	if( bytesLeft() < 2 )
+bool Bytestream::readShort (uint16& val) {
+	if (bytesLeft() < 2)
 		return false;
-	
+
 	val = 0;
-	
-	for( int i = 0; i < 2; ++i )
-		val |= *m_ptr++ << ( i * 8 );
+
+	for (int i = 0; i < 2; ++i)
+		val |= *m_ptr++ << (i * 8);
 
 	return true;
 }
 
 // =============================================================================
-bool Bytestream::readLong( uint32& val ) {
-	if( bytesLeft() < 4 )
+bool Bytestream::readLong (uint32& val) {
+	if (bytesLeft() < 4)
 		return false;
-	
+
 	val = 0;
-	
-	for( int i = 0; i < 4; ++i )
-		val |= *m_ptr++ << ( i * 8 );
-	
+
+	for (int i = 0; i < 4; ++i)
+		val |= *m_ptr++ << (i * 8);
+
 	return true;
 }
 
 // =============================================================================
-bool Bytestream::readFloat( float& val ) {
-	if( !readLong( g_floatunion.i ))
+bool Bytestream::readFloat (float& val) {
+	if (!readLong (g_floatunion.i))
 		return false;
 
 	val = g_floatunion.f;
@@ -122,104 +122,104 @@
 }
 
 // =============================================================================
-bool Bytestream::readString( str& val ) {
-	if( bytesLeft() < 1 ) // need at least the null terminator
+bool Bytestream::readString (str& val) {
+	if (bytesLeft() < 1)  // need at least the null terminator
 		return false;
-	
+
 	uint8_t c;
-	
-	while( readByte( c ) && c != '\0' )
-		val += ( char ) c;
-	
+
+	while (readByte (c) && c != '\0')
+		val += (char) c;
+
 	return true;
 }
 
 // =============================================================================
-void Bytestream::doWrite( uint8_t val ) {
+void Bytestream::doWrite (uint8_t val) {
 	*m_ptr++ = val;
 	m_len++;
 }
 
-void Bytestream::growToFit( ulong bytes ) {
-	if( spaceLeft() < bytes )
-		resize( m_size + bytes + 128 );
+void Bytestream::growToFit (ulong bytes) {
+	if (spaceLeft() < bytes)
+		resize (m_size + bytes + 128);
 }
 
-bool Bytestream::readBytes( uint8 numbytes, uint8* val ) {
-	while( numbytes-- )
-		if( !readByte( *val++ ))
+bool Bytestream::readBytes (uint8 numbytes, uint8* val) {
+	while (numbytes--)
+		if (!readByte (*val++))
 			return false;
-	
+
 	return true;
 }
 
-void Bytestream::writeBytes( uint8 numbytes, const uint8* val ) {
-	growToFit( numbytes );
-	
-	while( numbytes-- )
-		writeByte( *val++ );
+void Bytestream::writeBytes (uint8 numbytes, const uint8* val) {
+	growToFit (numbytes);
+
+	while (numbytes--)
+		writeByte (*val++);
 }
 
 // =============================================================================
-void Bytestream::writeByte( uint8 val ) {
-	growToFit( 1 );
-	doWrite( val );
+void Bytestream::writeByte (uint8 val) {
+	growToFit (1);
+	doWrite (val);
 }
 
 // =============================================================================
-void Bytestream::writeShort( uint16 val ) {
-	growToFit( 2 );
-	
-	for( int i = 0; i < 2; ++i )
-		doWrite(( val >> ( i * 8 )) & 0xFF );
+void Bytestream::writeShort (uint16 val) {
+	growToFit (2);
+
+	for (int i = 0; i < 2; ++i)
+		doWrite ( (val >> (i * 8)) & 0xFF);
 }
 
 // =============================================================================
-void Bytestream::writeLong( uint32 val ) {
-	growToFit( 4 );
-	
-	for( int i = 0; i < 4; ++i )
-		doWrite(( val >> ( i * 8 )) & 0xFF );
+void Bytestream::writeLong (uint32 val) {
+	growToFit (4);
+
+	for (int i = 0; i < 4; ++i)
+		doWrite ( (val >> (i * 8)) & 0xFF);
 }
 
 // =============================================================================
-void Bytestream::writeFloat( float val ) {
+void Bytestream::writeFloat (float val) {
 	g_floatunion.f = val;
-	writeLong( g_floatunion.i );
+	writeLong (g_floatunion.i);
 }
 
 // =============================================================================
-void Bytestream::writeString( str val ) {
-	growToFit( val.length() + 1 );
-	
-	for( qchar c : val )
-		doWrite( c.toAscii() );
-	
-	doWrite( '\0' );
+void Bytestream::writeString (str val) {
+	growToFit (val.length() + 1);
+
+for (qchar c : val)
+		doWrite (c.toAscii());
+
+	doWrite ('\0');
 }
 
 // =============================================================================
-bool Bytestream::tryMerge( const Bytestream& other ) {
-	if( spaceLeft() < other.len() )
+bool Bytestream::tryMerge (const Bytestream& other) {
+	if (spaceLeft() < other.len())
 		return false;
-	
-	for( ulong i = 0; i < other.len(); ++i )
-		writeByte( other[i] );
-	
+
+	for (ulong i = 0; i < other.len(); ++i)
+		writeByte (other[i]);
+
 	return true;
 }
 
-void Bytestream::merge( const Bytestream& other ) {
-	growToFit( other.len() );
-	
-	if( !tryMerge( other )) {
+void Bytestream::merge (const Bytestream& other) {
+	growToFit (other.len());
+
+	if (!tryMerge (other)) {
 		// Shouldn't happen
-		fprint( stderr, "ByteStream: Not enough space for merge (%1 bytes left, need %2)",
-				spaceLeft(), other.len() );
+		fprint (stderr, "ByteStream: Not enough space for merge (%1 bytes left, need %2)",
+				spaceLeft(), other.len());
 		abort();
 	}
 }
 
 const uint8* Bytestream::data() const {
 	return m_data;
-}
\ No newline at end of file
+}
--- a/src/config.cpp	Wed Jul 17 19:45:19 2013 +0300
+++ b/src/config.cpp	Sun Aug 11 02:58:55 2013 +0300
@@ -1,57 +1,56 @@
 #include <QLabel>
 #include <QFileDialog>
 #include <QFormLayout>
+#include <QProgressBar>
+#include <QMessageBox>
+#include <QUrl>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
 #include "config.h"
 #include "ui_configbox.h"
 #include "misc.h"
+#include "demo.h"
+#include "build/moc_config.cpp"
 
 // =============================================================================
 // -----------------------------------------------------------------------------
 class FindPathButton : public QPushButton {
 public:
-	explicit FindPathButton( QWidget* parent = null ) : QPushButton( parent ) {
-		setText( "..." );
+	explicit FindPathButton (QWidget* parent = null) : QPushButton (parent) {
+		setText ("...");
 	}
-	
-	QLineEdit* editWidget() const { return m_editWidget; }
-	void setEditWidget( QLineEdit* edit ) { m_editWidget = edit; }
-	
+
+	QLineEdit* editWidget() const {
+		return m_editWidget;
+	}
+	void setEditWidget (QLineEdit* edit) {
+		m_editWidget = edit;
+	}
+
 private:
 	QLineEdit* m_editWidget;
 };
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-ConfigBox::ConfigBox( QWidget* parent, Qt::WindowFlags f ) : QDialog( parent, f ) {
+ConfigBox::ConfigBox (QWidget* parent, Qt::WindowFlags f) : QDialog (parent, f),
+	m_nam (new QNetworkAccessManager (this)) {
 	ui = new Ui_ConfigBox;
-	ui->setupUi( this );
-	QFormLayout* layout = new QFormLayout( ui->zandronumVersions );
-	
-	for( str ver : g_zanVersions ) {
-		QLabel* lb = new QLabel( ver + ":" );
-		QLineEdit* ledit = new QLineEdit;
-		FindPathButton* btn = new FindPathButton;
-		btn->setEditWidget( ledit );
-		
-		QWidget* wdg = new QWidget;
-		QHBoxLayout* leditLayout = new QHBoxLayout( wdg );
-		leditLayout->addWidget( ledit );
-		leditLayout->addWidget( btn );
-		
-		m_zanBinaries << ledit;
-		layout->addRow( lb, wdg );
-		connect( btn, SIGNAL( clicked() ), this, SLOT( findZanBinary() ));
-	}
-	
+	ui->setupUi (this);
+	ui->updateProgress->hide();
+	ui->updateLabel->hide();
+
+	initVersions();
 	initFromSettings();
-	
-	connect( ui->wad_add, SIGNAL( clicked() ), this, SLOT( addPath() ));
-	connect( ui->wad_pathEntry, SIGNAL( returnPressed() ), this, SLOT( addPath() ));
-	connect( ui->wad_findPath, SIGNAL( clicked() ), this, SLOT( findPath() ));
-	connect( ui->wad_del, SIGNAL( clicked() ), this, SLOT( delPath() ));
-	connect( ui->buttonBox, SIGNAL( accepted() ), this, SLOT( okPressed() ));
-	connect( ui->buttonBox, SIGNAL( rejected() ), this, SLOT( cancelPressed() ));
-	setWindowTitle( fmt( APPNAME " %1", versionString()));
+
+	connect (ui->wad_add, SIGNAL (clicked()), this, SLOT (addPath()));
+	connect (ui->wad_pathEntry, SIGNAL (returnPressed()), this, SLOT (addPath()));
+	connect (ui->wad_findPath, SIGNAL (clicked()), this, SLOT (findPath()));
+	connect (ui->wad_del, SIGNAL (clicked()), this, SLOT (delPath()));
+	connect (ui->buttonBox, SIGNAL (clicked (QAbstractButton*)), this,
+			 SLOT (buttonPressed (QAbstractButton*)));
+	setWindowTitle (fmt (APPNAME " %1", versionString()));
 }
 
 // =============================================================================
@@ -62,45 +61,104 @@
 
 // =============================================================================
 // -----------------------------------------------------------------------------
+void ConfigBox::initVersions() {
+	QFormLayout* releaseLayout = new QFormLayout (ui->zandronumVersions),
+	*testLayout = new QFormLayout (ui->betaVersions);
+	list<var> versions = getVersionsList(),
+		releases = getReleasesList();
+	
+	for (const var& ver : versions) {
+		str verstring = ver.toString();
+		
+		bool isRelease = false;
+		for (const var& rel : releases) {
+			if (rel.toString() == verstring) {
+				isRelease = true;
+				break;
+			}
+		}
+		
+		QLabel* lb = new QLabel (verstring + ":");
+		QLineEdit* ledit = new QLineEdit;
+		FindPathButton* btn = new FindPathButton;
+		btn->setEditWidget (ledit);
+
+		QWidget* wdg = new QWidget;
+		QHBoxLayout* leditLayout = new QHBoxLayout (wdg);
+		leditLayout->addWidget (ledit);
+		leditLayout->addWidget (btn);
+
+		m_zanBinaries << ledit;
+		connect (btn, SIGNAL (clicked()), this, SLOT (findZanBinary()));
+
+		if (isRelease)
+			releaseLayout->addRow (lb, wdg);
+		else
+			testLayout->addRow (lb, wdg);
+	}
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
 void ConfigBox::initFromSettings() {
 	QSettings cfg;
-	
 	ui->wad_pathsList->clear();
+	list<var> paths = cfg.value ("wads/paths", list<var>()).toList();
 	
-	list<var> paths = cfg.value( "wads/paths", list<var>() ).toList();
-	for( const var& it : paths )
-		addPath( it.toString() );
+	for (const var & it : paths)
+		addPath (it.toString());
 	
 	int i = 0;
-	for( str ver : g_zanVersions )
-		m_zanBinaries[i++]->setText( cfg.value( binaryConfigName( ver ), "" ).toString() );
+	
+	list<var> versions = getVersionsList();
+	for (const var& ver : versions)
+		m_zanBinaries[i++]->setText (cfg.value (binaryConfigName (ver.toString()), "").toString());
 	
-	ui->noDemoPrompt->setChecked( cfg.value( "nodemoprompt", false ).toBool() );
+	ui->noDemoPrompt->setChecked (cfg.value ("nodemoprompt", false).toBool());
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+void ConfigBox::saveSettings() {
+	QSettings cfg;
+	list<var> wadPathList;
+	
+	for (int i = 0; i < ui->wad_pathsList->count(); ++i)
+		wadPathList << ui->wad_pathsList->item (i)->text();
+	
+	cfg.setValue ("wads/paths", wadPathList);
+	cfg.setValue ("nodemoprompt", ui->noDemoPrompt->isChecked());
+	
+	int i = 0;
+	list<var> versions = getVersionsList();
+	for (const var& ver : versions)
+		cfg.setValue (binaryConfigName (ver.toString()), m_zanBinaries[i++]->text());
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
 void ConfigBox::addPath() {
-	addPath( ui->wad_pathEntry->text() );
+	addPath (ui->wad_pathEntry->text());
 	ui->wad_pathEntry->clear();
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-void ConfigBox::addPath( str path ) {
-	ui->wad_pathsList->addItem( path );
-	QListWidgetItem* item = ui->wad_pathsList->item( ui->wad_pathsList->count() - 1 );
-	item->setFlags( item->flags() | Qt::ItemIsEditable );
+void ConfigBox::addPath (str path) {
+	ui->wad_pathsList->addItem (path);
+	QListWidgetItem* item = ui->wad_pathsList->item (ui->wad_pathsList->count() - 1);
+	item->setFlags (item->flags() | Qt::ItemIsEditable);
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
 void ConfigBox::findPath() {
-	str path = QFileDialog::getExistingDirectory( this );
-	if( path.isEmpty() )
+	str path = QFileDialog::getExistingDirectory (this);
+
+	if (path.isEmpty())
 		return;
-	
-	ui->wad_pathEntry->setText( path );
+
+	ui->wad_pathEntry->setText (path);
 }
 
 // =============================================================================
@@ -112,46 +170,40 @@
 // =============================================================================
 // -----------------------------------------------------------------------------
 void ConfigBox::findZanBinary() {
-	FindPathButton* btn = dynamic_cast<FindPathButton*>( sender() );
-	str path;
+	FindPathButton* btn = dynamic_cast<FindPathButton*> (sender());
 	
-	if( !btn )
+	if (!btn)
 		return;
 	
-	str filter;
-#ifdef _WIN32
-	filter = "Zandronum Binaries (zandronum.exe)(zandronum.exe);;All files (*.*)(*.*)";
-#else
-	filter = "Zandronum Binaries (zandronum)(zandronum);;All files (*.*)(*.*)";
-#endif
-	
-	if(( path = QFileDialog::getOpenFileName( this, QString(), QString(), filter )).isEmpty() )
+	str path = getBinaryPath (this);
+	if (path.isEmpty())
 		return;
 	
-	btn->editWidget()->setText( path );
+	btn->editWidget()->setText (path);
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-void ConfigBox::okPressed() {
-	QSettings cfg;
-	list<var> wadPathList;
-	
-	for( int i = 0; i < ui->wad_pathsList->count(); ++i )
-		wadPathList << ui->wad_pathsList->item( i )->text();
+str ConfigBox::getBinaryPath (QWidget* parent) {
+	str path;
+	const str filter =
+#ifdef _WIN32
+		"Zandronum Binaries (zandronum.exe)(zandronum.exe);;All files (*.*)(*.*)";
+#else
+		"Zandronum Binaries (zandronum)(zandronum);;All files (*.*)(*.*)";
+#endif
 	
-	cfg.setValue( "wads/paths", wadPathList );
-	cfg.setValue( "nodemoprompt", ui->noDemoPrompt->isChecked() );
-	
-	int i = 0;
-	for( str ver : g_zanVersions )
-		cfg.setValue( binaryConfigName( ver ), m_zanBinaries[i++]->text() );
-	
-	accept();
+	return QFileDialog::getOpenFileName (parent, QString(), QString(), filter);
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-void ConfigBox::cancelPressed() {
-	reject();
+void ConfigBox::buttonPressed (QAbstractButton* btn) {
+	if (btn == ui->buttonBox->button (QDialogButtonBox::Ok)) {
+		saveSettings();
+		accept();
+	} elif (btn == ui->buttonBox->button (QDialogButtonBox::Cancel))
+		reject();
+	elif (btn == ui->buttonBox->button (QDialogButtonBox::Apply))
+		saveSettings();
 }
\ No newline at end of file
--- a/src/config.h	Wed Jul 17 19:45:19 2013 +0300
+++ b/src/config.h	Sun Aug 11 02:58:55 2013 +0300
@@ -5,8 +5,13 @@
 #include "main.h"
 #include "types.h"
 
+class QNetworkReply;
+class QHBoxLayout;
+class QLabel;
+class QAbstractButton;
 class QLineEdit;
 class Ui_ConfigBox;
+class QNetworkAccessManager;
 
 class ConfigBox : public QDialog {
 	Q_OBJECT
@@ -16,18 +21,23 @@
 	virtual ~ConfigBox();
 	void addPath( str path );
 	void initFromSettings();
+	void saveSettings();
+	void initVersions();
+	
+	static str getBinaryPath (QWidget* parent);
 	
 public slots:
 	void addPath();
 	void findPath();
 	void delPath();
 	void findZanBinary();
-	void okPressed();
-	void cancelPressed();
+	void buttonPressed( QAbstractButton* btn );
 	
 private:
 	Ui_ConfigBox* ui;
 	list<QLineEdit*> m_zanBinaries;
+	QNetworkAccessManager* m_nam;
+	QNetworkReply* m_reply;
 };
 
 #endif // CONFIG_H
\ No newline at end of file
--- a/src/demo.cpp	Wed Jul 17 19:45:19 2013 +0300
+++ b/src/demo.cpp	Sun Aug 11 02:58:55 2013 +0300
@@ -6,49 +6,75 @@
 #include "bytestream.h"
 #include "misc.h"
 #include "ui_demoprompt.h"
+#include "prompts.h"
 
-static const uint32 g_demoSignature = makeByteID( 'Z', 'C', 'L', 'D' );
+static const uint32 g_demoSignature = makeByteID ('Z', 'C', 'L', 'D');
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-static str tr( const char* msg ) {
-	return QObject::tr( msg );
+str uncolorize (const str& in) {
+	str out;
+	int skip = 0;
+	
+	for (const qchar& c : in) {
+		if (skip-- > 0)
+			continue;
+		
+		if (c.toAscii() == '\034') {
+			skip = 1;
+			continue;
+		}
+		
+		out += c;
+	}
+	
+	return out;
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-static void error( str msg ) {
-	QMessageBox::critical( null, "Error", msg );
+static str tr (const char* msg) {
+	return QObject::tr (msg);
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-static int getVersionIndex( str ver ) {
-	int i;
-	
-	for( i = 0; i < g_zanVersions.size(); ++i )
-		if( g_zanVersions[i] == ver )
-			return i;
-	
-	return -1;
+static void error (str msg) {
+	QMessageBox::critical (null, "Error", msg);
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-static str findWAD( str name ) {
+static bool isKnownVersion (str ver) {
 	QSettings cfg;
-	list<var> paths = cfg.value( "wads/paths", list<var>() ).toList();
+	list<var> versions = getVersionsList();
+	
+	for (const var& it : versions)
+		if (it.toString() == ver || it.toString() + 'M' == ver)
+			return true;
 	
-	if( paths.size() == 0 ) {
-		error( tr( "No WAD paths configured!" ));
-		exit( 9 );
+	return false;
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+static str findWAD (str name) {
+	QSettings cfg;
+	list<var> paths = cfg.value ("wads/paths", list<var>()).toList();
+	
+	if (paths.size() == 0) {
+		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( var it : paths ) {
-		str fullpath = fmt( "%1/%2", it.toString(), name );
-		QFile f( fullpath );
+	for (const var& it : paths) {
+		str fullpath = fmt ("%1/%2", it.toString(), name);
+		QFile f (fullpath);
 		
-		if( f.exists() )
+		if (f.exists())
 			return fullpath;
 	}
 	
@@ -57,27 +83,27 @@
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-int launchDemo( str path ) {
-	FILE* fp = fopen( path.toStdString().c_str(), "r" );
+int launchDemo (str path) {
+	FILE* fp = fopen (path.toStdString().c_str(), "r");
 	
-	if( !fp ) {
-		error( fmt( tr( "Couldn't open '%1' for reading: %2" ), path, strerror( errno )));
+	if (!fp) {
+		error (fmt (tr ("Couldn't open '%1' for reading: %2"), path, strerror (errno)));
 		return 1;
 	}
 	
-	fseek( fp, 0, SEEK_END );
-	const size_t fsize = ftell( fp );
-	rewind( fp );
+	fseek (fp, 0, SEEK_END);
+	const size_t fsize = ftell (fp);
+	rewind (fp);
 	
 	char* buf = new char[fsize];
 	
-	if( fread( buf, 1, fsize, fp ) != fsize ) {
-		error( tr( "I/O error" ));
+	if (fread (buf, 1, fsize, fp) != fsize) {
+		error (tr ("I/O error"));
 		delete[] buf;
 		return 2;
 	}
-	
-	Bytestream s( buf, fsize );
+
+	Bytestream s (buf, fsize);
 	delete[] buf;
 	
 	uint8 offset;
@@ -93,8 +119,8 @@
 	{
 		uint32 sig;
 		
-		if( !s.readLong( sig ) || sig != g_demoSignature ) {
-			error( fmt( tr( "'%1' is not a Zandronum demo file!" ), path ));
+		if (!s.readLong (sig) || sig != g_demoSignature) {
+			error (fmt (tr ("'%1' is not a Zandronum demo file!"), path));
 			return 3;
 		}
 	}
@@ -102,108 +128,123 @@
 	// Zandronum stores CLD_DEMOLENGTH after the signature. This is also the
 	// first demo enumerator, so we can determine the offset (which is variable!)
 	// from this byte
-	s.readByte( offset );
-	s.readLong( length );
-	
+	s.readByte (offset);
+	s.readLong (length);
+
 	uint16 zanversionID, numWads;
 	uint32 longSink;
 	str zanversion;
 	list<str> wads;
 	bool ready = false;
+	uint8 buildID;
 	
-	for( ;; ) {
+	// Read the demo header and get data
+	for (;;) {
 		uint8 header;
-		if( !s.readByte( header ))
+		
+		if (!s.readByte (header))
 			break;
 		
-		if( header == DemoBodyStart + offset ) {
+		if (header == DemoBodyStart + offset) {
 			ready = true;
 			break;
-		} elif( header == DemoVersion + offset ) {
-			s.readShort( zanversionID );
-			s.readString( zanversion );
-			s.readLong( longSink ); // rng seed - we don't need it
-		} elif( header == DemoUserInfo + offset ) {
-			s.readString( userinfo.netname );
-			s.readByte( userinfo.gender );
-			s.readLong( userinfo.color );
-			s.readLong( userinfo.aimdist );
-			s.readString( userinfo.skin );
-			s.readLong( userinfo.railcolor );
-			s.readByte( userinfo.handicap );
-			s.readByte( userinfo.unlagged );
-			s.readByte( userinfo.respawnOnFire );
-			s.readByte( userinfo.ticsPerUpdate );
-			s.readByte( userinfo.connectionType );
-			s.readString( userinfo.className );
-		} elif( header == DemoWads + offset ) {
-			s.readShort( numWads );
+		} elif (header == DemoVersion + offset) {
+			s.readShort (zanversionID);
+			s.readString (zanversion);
+			
+			if (zanversion.left (4) != "1.1-" && zanversion.left (6) != "1.1.1-")
+				s.readByte (buildID);
+			else
+				buildID = 1;
 			
-			for( uint8 i = 0; i < numWads; ++i ) {
+			s.readLong (longSink);  // rng seed - we don't need it
+		} elif (header == DemoUserInfo + offset) {
+			s.readString (userinfo.netname);
+			s.readByte (userinfo.gender);
+			s.readLong (userinfo.color);
+			s.readLong (userinfo.aimdist);
+			s.readString (userinfo.skin);
+			s.readLong (userinfo.railcolor);
+			s.readByte (userinfo.handicap);
+			s.readByte (userinfo.unlagged);
+			s.readByte (userinfo.respawnOnFire);
+			s.readByte (userinfo.ticsPerUpdate);
+			s.readByte (userinfo.connectionType);
+			s.readString (userinfo.className);
+		} elif (header == DemoWads + offset) {
+			str sink;
+			s.readShort (numWads);
+			
+			for (uint8 i = 0; i < numWads; ++i) {
 				str wad;
-				s.readString( wad );
+				s.readString (wad);
 				wads << wad;
 			}
 			
 			// The demo has two checksum strings. We're not interested
 			// in them though.
-			str sink;
-			for( int i = 0; i < 2; ++i )
-				s.readString( sink );
+			for (int i = 0; i < 2; ++i)
+				s.readString (sink);
 		} else {
-			error( fmt( tr( "Unknown header %1!\n" ), (int) header ));
-			return 4;
+			error (fmt (tr ("Unknown header %1!\n"), (int) header));
+			return 3;
 		}
 	}
-	
-	if( !ready ) {
-		error( fmt( tr( "Incomplete demo header in '%s'!" ), path ));
-		return 5;
-	}
-	
-	int i = getVersionIndex( zanversion );
-	if( i == -1 ) {
-		error( fmt( tr( "Unknown Zandronum version %1!\n" ), zanversion ));
-		return 6;
+
+	if (!ready) {
+		error (fmt (tr ("Incomplete demo header in '%s'!"), path));
+		return 3;
 	}
 	
+	if (!isKnownVersion (zanversion)) {
+		UnknownVersionPrompt* prompt = new UnknownVersionPrompt (path, zanversion, (buildID == 1));
+		if (!prompt->exec())
+			return 6;
+		
+		if (!isKnownVersion (zanversion)) {
+			error (tr ("Failure in configuration! This shouldn't happen."));
+			return 6;
+		}
+	}
+
 	QSettings cfg;
-	str binarypath = cfg.value( binaryConfigName( zanversion )).toString();
-	
-	if( binarypath.isEmpty() ) {
-		error( fmt( tr( "No binary path specified for Zandronum version %1!\n" ), zanversion ));
+	str binarypath = cfg.value (binaryConfigName (zanversion)).toString();
+
+	if (binarypath.isEmpty()) {
+		error (fmt (tr ("No binary path specified for Zandronum version %1!"), zanversion));
 		return 7;
 	}
-	
-	str iwad, iwadpath;
-	list<str> pwads, pwadpaths;
+
+	str iwadpath;
+	list<str> pwadpaths;
 	
 	// Find the WADs
-	for( const str& wad : wads ) {
-		str path = findWAD( wad );
+	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() )
-			path = findWAD( wad.toUpper() );
+		if (&wad == &wads[0] && path.isEmpty())
+			path = findWAD (wad.toUpper());
 		
-		if( path.isEmpty() ) {
-			error( fmt( tr( "Couldn't find %1!\n" ), wad ));
+		if (path.isEmpty()) {
+			error (fmt (tr ("Couldn't find %1!"), wad));
 			return 8;
 		}
 		
-		if( &wad == &wads[0] ) {
+		if (&wad == &wads[0])
 			iwadpath = path;
-			iwad = wad;
-		} else {
+		else
 			pwadpaths << path;
-			pwads << wad;
-		}
 	}
 	
-	if( !cfg.value( "nodemoprompt", false ).toBool() ) {
+	if (!cfg.value ("nodemoprompt", false).toBool()) {
 		str pwadtext;
-		for( const str& pwad : pwads ) {
-			if( !pwadtext.isEmpty() )
+		
+		for (const str& pwad : wads) {
+			if (&pwad == &wads[0])
+				continue; // skip the IWAD
+			
+			if (!pwadtext.isEmpty())
 				pwadtext += "<br />";
 			
 			pwadtext += pwad;
@@ -211,33 +252,31 @@
 		
 		QDialog* dlg = new QDialog;
 		Ui_DemoPrompt ui;
-		ui.setupUi( dlg );
-		ui.demoNameLabel->setText( basename( path ));
-		ui.demoRecorder->setText( userinfo.netname );
-		ui.versionLabel->setText( zanversion );
-		ui.iwadLabel->setText( wads[0] );
-		ui.pwadsLabel->setText( pwadtext );
+		ui.setupUi (dlg);
+		ui.demoNameLabel->setText (basename (path));
+		ui.demoRecorder->setText (uncolorize (userinfo.netname));
+		ui.versionLabel->setText (zanversion);
+		ui.iwadLabel->setText (wads[0]);
+		ui.pwadsLabel->setText (pwadtext);
+		dlg->setWindowTitle (fmt (APPNAME " %1", versionString()));
 		
-		if( !dlg->exec() )
+		if (!dlg->exec())
 			return 1;
 	}
-	
-	print( "binary: %1\n", binarypath );
-	print( "iwad: %1\npwads: %2\n", iwadpath, pwadpaths );
-	
-	QStringList cmdlineList ({
+
+	QStringList cmdlineList ( {
 		"-playdemo", path,
 		"-iwad", iwadpath,
 	});
 	
-	if( pwadpaths.size() > 0 ) {
+	if (pwadpaths.size() > 0) {
 		cmdlineList << "-file";
 		cmdlineList << pwadpaths;
 	}
 	
-	print( "commandline: %1 %2\n", binarypath, cmdlineList.join( " " ));
+	print ("Executing: %1 %2\n", binarypath, cmdlineList.join (" "));
 	QProcess* proc = new QProcess;
-	proc->start( binarypath, cmdlineList );
-	proc->waitForFinished( -1 );
+	proc->start (binarypath, cmdlineList);
+	proc->waitForFinished (-1);
 	return 0;
 }
\ No newline at end of file
--- a/src/demo.h	Wed Jul 17 19:45:19 2013 +0300
+++ b/src/demo.h	Sun Aug 11 02:58:55 2013 +0300
@@ -1,5 +1,6 @@
 #ifndef DEMO_H
 #define DEMO_H
+
 #include "types.h"
 
 enum {
@@ -16,6 +17,12 @@
 	DemoWads
 };
 
-int launchDemo( str path );
+struct VersionInfo {
+	str shortVersion;
+	str versionString;
+	bool release;
+};
+
+int launchDemo (str path);
 
 #endif // DEMO_H
\ No newline at end of file
--- a/src/main.cpp	Wed Jul 17 19:45:19 2013 +0300
+++ b/src/main.cpp	Sun Aug 11 02:58:55 2013 +0300
@@ -4,10 +4,6 @@
 #include "config.h"
 #include "demo.h"
 
-const list<str> g_zanVersions ({
-	"1.1-r130716-1906M",
-});
-
 // =============================================================================
 // -----------------------------------------------------------------------------
 int main( int argc, char* argv[] ) {
--- a/src/main.h	Wed Jul 17 19:45:19 2013 +0300
+++ b/src/main.h	Sun Aug 11 02:58:55 2013 +0300
@@ -19,9 +19,9 @@
 
 #include <QSettings>
 
+struct VersionInfo;
 static const std::nullptr_t null = nullptr;
 
-extern const QList<QString> g_zanVersions;
 QString versionString();
 
 #endif // MAIN_H
\ No newline at end of file
--- a/src/misc.cpp	Wed Jul 17 19:45:19 2013 +0300
+++ b/src/misc.cpp	Sun Aug 11 02:58:55 2013 +0300
@@ -1,22 +1,54 @@
 #include "misc.h"
 
-uint32 makeByteID( uint8 a, uint8 b, uint8 c, uint8 d ) {
-	return a | ( b << 8 ) | ( c << 16 ) | ( d << 24 );
+uint32 makeByteID (uint8 a, uint8 b, uint8 c, uint8 d) {
+	return a | (b << 8) | (c << 16) | (d << 24);
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+str binaryConfigName (str ver) {
+	return fmt ("binaries/%1", ver);
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+list<var> getVersionsList() {
+	QSettings cfg;
+	return cfg.value ("binarynames", list<var>()).toList();
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-str binaryConfigName( str ver ) {
-	return fmt( "binaries/%1", ver );
+list<var> getReleasesList() {
+	QSettings cfg;
+	return cfg.value ("releasenames", list<var>()).toList();
 }
 
 // =============================================================================
 // -----------------------------------------------------------------------------
-str basename( str path ) {
-	long lastpos = path.lastIndexOf( "/" );
+void addVersion (str name, bool isRelease, str binaryPath) {
+	QSettings cfg;
+	list<var> versions = getVersionsList();
+	versions << var (name);
+	cfg.setValue ("binarynames", versions);
+	cfg.setValue (binaryConfigName (name), binaryPath);
 	
-	if( lastpos != -1 )
-		return path.mid( lastpos + 1 );
+	if (isRelease) {
+		versions = getReleasesList();
+		versions << var (name);
+		cfg.setValue ("releasenames", versions);
+	}
+	
+	cfg.sync();
+}
+
+// =============================================================================
+// -----------------------------------------------------------------------------
+str basename (str path) {
+	long lastpos = path.lastIndexOf ("/");
+	
+	if (lastpos != -1)
+		return path.mid (lastpos + 1);
 	
 	return path;
-}
\ No newline at end of file
+}
--- a/src/misc.h	Wed Jul 17 19:45:19 2013 +0300
+++ b/src/misc.h	Sun Aug 11 02:58:55 2013 +0300
@@ -3,9 +3,12 @@
 
 #include "types.h"
 
-uint32 makeByteID( uint8 a, uint8 b, uint8 c, uint8 d );
-str binaryConfigName( str ver );
-str basename( str path );
+uint32 makeByteID (uint8 a, uint8 b, uint8 c, uint8 d);
+str binaryConfigName (str ver);
+str basename (str path);
+list<var> getVersionsList();
+list<var> getReleasesList();
+void addVersion (str name, bool isRelease, str binaryPath);
 
 // -----------------------------------------------------------------------------
 // Templated clamp
--- a/src/src.pro	Wed Jul 17 19:45:19 2013 +0300
+++ b/src/src.pro	Sun Aug 11 02:58:55 2013 +0300
@@ -9,5 +9,6 @@
 RCC_DIR         = ./build/
 SOURCES         = *.cpp
 HEADERS         = *.h
+QT             += network
 FORMS           = ui/*.ui
 QMAKE_CXXFLAGS += -std=c++0x
\ No newline at end of file
--- a/src/ui/configbox.ui	Wed Jul 17 19:45:19 2013 +0300
+++ b/src/ui/configbox.ui	Sun Aug 11 02:58:55 2013 +0300
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>480</width>
-    <height>320</height>
+    <width>544</width>
+    <height>308</height>
    </rect>
   </property>
   <property name="minimumSize">
@@ -115,11 +115,14 @@
      </widget>
      <widget class="QWidget" name="tab_2">
       <attribute name="title">
-       <string>Zandronum Binaries</string>
+       <string>Release Binaries</string>
       </attribute>
       <layout class="QVBoxLayout" name="verticalLayout_4">
        <item>
         <widget class="QScrollArea" name="scrollArea">
+         <property name="frameShape">
+          <enum>QFrame::NoFrame</enum>
+         </property>
          <property name="widgetResizable">
           <bool>true</bool>
          </property>
@@ -128,8 +131,8 @@
            <rect>
             <x>0</x>
             <y>0</y>
-            <width>96</width>
-            <height>26</height>
+            <width>516</width>
+            <height>213</height>
            </rect>
           </property>
           <layout class="QVBoxLayout" name="verticalLayout_5">
@@ -142,6 +145,38 @@
        </item>
       </layout>
      </widget>
+     <widget class="QWidget" name="tab_4">
+      <attribute name="title">
+       <string>Testing Binaries</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_7">
+       <item>
+        <widget class="QScrollArea" name="scrollArea_2">
+         <property name="frameShape">
+          <enum>QFrame::NoFrame</enum>
+         </property>
+         <property name="widgetResizable">
+          <bool>true</bool>
+         </property>
+         <widget class="QWidget" name="scrollAreaWidgetContents_2">
+          <property name="geometry">
+           <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>516</width>
+            <height>213</height>
+           </rect>
+          </property>
+          <layout class="QVBoxLayout" name="verticalLayout_8">
+           <item>
+            <widget class="QWidget" name="betaVersions" native="true"/>
+           </item>
+          </layout>
+         </widget>
+        </widget>
+       </item>
+      </layout>
+     </widget>
      <widget class="QWidget" name="tab_3">
       <attribute name="title">
        <string>Misc</string>
@@ -153,7 +188,7 @@
           <string>If this is not set, a prompt showing demo info is displayed first.</string>
          </property>
          <property name="text">
-          <string>Launch immediately</string>
+          <string>Launch without prompt</string>
          </property>
         </widget>
        </item>
@@ -175,14 +210,66 @@
     </widget>
    </item>
    <item>
-    <widget class="QDialogButtonBox" name="buttonBox">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-     <property name="standardButtons">
-      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
-     </property>
-    </widget>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QWidget" name="widget" native="true">
+       <layout class="QHBoxLayout" name="horizontalLayout_3">
+        <item>
+         <widget class="QLabel" name="updateLabel">
+          <property name="text">
+           <string>Checking for updates... </string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QProgressBar" name="updateProgress">
+          <property name="enabled">
+           <bool>true</bool>
+          </property>
+          <property name="maximumSize">
+           <size>
+            <width>16777215</width>
+            <height>16</height>
+           </size>
+          </property>
+          <property name="maximum">
+           <number>0</number>
+          </property>
+          <property name="value">
+           <number>-1</number>
+          </property>
+          <property name="textVisible">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
    </item>
   </layout>
  </widget>

mercurial