src/demo.cpp

changeset 6
67b6ef6917ba
child 8
e8f645d9f28f
equal deleted inserted replaced
5:3c04e05ab24f 6:67b6ef6917ba
1 #include <QFile>
2 #include <QDataStream>
3 #include <QMessageBox>
4 #include <QProcess>
5 #include "demo.h"
6 #include "bytestream.h"
7 #include "misc.h"
8
9 static const uint32 g_demoSignature = makeByteID( 'Z', 'C', 'L', 'D' );
10
11 // =============================================================================
12 // -----------------------------------------------------------------------------
13 static str tr( const char* msg ) {
14 return QObject::tr( msg );
15 }
16
17 // =============================================================================
18 // -----------------------------------------------------------------------------
19 static void error( str msg ) {
20 QMessageBox::critical( null, "Error", msg );
21 }
22
23 // =============================================================================
24 // -----------------------------------------------------------------------------
25 static int getVersionIndex( str ver ) {
26 int i;
27
28 for( i = 0; i < g_zanVersions.size(); ++i )
29 if( g_zanVersions[i] == ver )
30 return i;
31
32 return -1;
33 }
34
35 // =============================================================================
36 // -----------------------------------------------------------------------------
37 static str findWAD( str name ) {
38 QSettings cfg;
39 list<var> paths = cfg.value( "wads/paths", list<var>() ).toList();
40
41 if( paths.size() == 0 ) {
42 error( tr( "No WAD paths configured!" ));
43 exit( 9 );
44 }
45
46 for( var it : paths ) {
47 str fullpath = fmt( "%1/%2", it.toString(), name );
48 QFile f( fullpath );
49
50 if( f.exists() )
51 return fullpath;
52 }
53
54 return "";
55 }
56
57 // =============================================================================
58 // -----------------------------------------------------------------------------
59 int launchDemo( str path ) {
60 FILE* fp = fopen( path.toStdString().c_str(), "r" );
61
62 if( !fp ) {
63 error( fmt( tr( "Couldn't open '%1' for reading: %2" ), path, strerror( errno )));
64 return 1;
65 }
66
67 fseek( fp, 0, SEEK_END );
68 const size_t fsize = ftell( fp );
69 rewind( fp );
70
71 char* buf = new char[fsize];
72
73 if( fread( buf, 1, fsize, fp ) != fsize ) {
74 error( tr( "I/O error" ));
75 delete[] buf;
76 return 2;
77 }
78
79 Bytestream s( buf, fsize );
80 delete[] buf;
81
82 uint8 offset;
83 uint32 length;
84
85 struct {
86 str netname, skin, className;
87 uint8 gender, handicap, unlagged, respawnOnFire, ticsPerUpdate, connectionType;
88 uint32 color, aimdist, railcolor;
89 } userinfo;
90
91 // Check signature
92 {
93 uint32 sig;
94
95 if( !s.readLong( sig ) || sig != g_demoSignature ) {
96 error( fmt( tr( "'%1' is not a Zandronum demo file!" ), path ));
97 return 3;
98 }
99 }
100
101 // Zandronum stores CLD_DEMOLENGTH after the signature. This is also the
102 // first demo enumerator, so we can determine the offset (which is variable!)
103 // from this byte
104 s.readByte( offset );
105 s.readLong( length );
106
107 uint16 zanversionID, numWads;
108 uint32 longSink;
109 str zanversion;
110 list<str> wads;
111 bool ready = false;
112
113 for( ;; ) {
114 uint8 header;
115 if( !s.readByte( header ))
116 break;
117
118 if( header == DemoBodyStart + offset ) {
119 ready = true;
120 break;
121 } elif( header == DemoVersion + offset ) {
122 s.readShort( zanversionID );
123 s.readString( zanversion );
124 s.readLong( longSink ); // rng seed - we don't need it
125 } elif( header == DemoUserInfo + offset ) {
126 s.readString( userinfo.netname );
127 s.readByte( userinfo.gender );
128 s.readLong( userinfo.color );
129 s.readLong( userinfo.aimdist );
130 s.readString( userinfo.skin );
131 s.readLong( userinfo.railcolor );
132 s.readByte( userinfo.handicap );
133 s.readByte( userinfo.unlagged );
134 s.readByte( userinfo.respawnOnFire );
135 s.readByte( userinfo.connectionType );
136 s.readString( userinfo.className );
137 } elif( header == DemoWads + offset ) {
138 s.readShort( numWads );
139
140 for( uint8 i = 0; i < numWads; ++i ) {
141 str wad;
142 s.readString( wad );
143 wads << wad;
144 }
145
146 // The demo has two checksum strings. We're not interested
147 // in them though.
148 str sink;
149 for( int i = 0; i < 2; ++i )
150 s.readString( sink );
151 } else {
152 error( fmt( tr( "Unknown header %1!\n" ), (int) header ));
153 return 4;
154 }
155 }
156
157 if( !ready ) {
158 error( fmt( tr( "Incomplete demo header in '%s'!" ), path ));
159 return 5;
160 }
161
162 int i = getVersionIndex( zanversion );
163 if( i == -1 ) {
164 error( fmt( tr( "Unknown Zandronum version %1!\n" ), zanversion ));
165 return 6;
166 }
167
168 QSettings cfg;
169 str binarypath = cfg.value( binaryConfigName( zanversion )).toString();
170
171 if( binarypath.isEmpty() ) {
172 error( fmt( tr( "No binary path specified for Zandronum version %1!\n" ), zanversion ));
173 return 7;
174 }
175
176 str iwadpath;
177 list<str> pwadpaths;
178
179 // Find the WADs
180 for( const str& wad : wads ) {
181 str path = findWAD( wad );
182
183 // IWAD names can appear in uppercase too. Linux is case-sensitive, so..
184 if( &wad == &wads[0] && path.isEmpty() )
185 path = findWAD( wad.toUpper() );
186
187 if( path.isEmpty() ) {
188 error( fmt( tr( "Couldn't find %1!\n" ), wad ));
189 return 8;
190 }
191
192 if( &wad == &wads[0] )
193 iwadpath = path;
194 else
195 pwadpaths << path;
196 }
197
198 print( "binary: %1\n", binarypath );
199 print( "iwad: %1\npwads: %2\n", iwadpath, pwadpaths );
200
201 QStringList cmdlineList ({
202 "-iwad",
203 iwadpath,
204 "-playdemo",
205 path
206 });
207
208 if( pwadpaths.size() > 0 ) {
209 cmdlineList << "-file";
210 cmdlineList << pwadpaths;
211 }
212
213 print( "commandline: %1 %2\n", binarypath, cmdlineList.join( " " ));
214 QProcess* proc = new QProcess;
215 proc->start( binarypath, cmdlineList );
216 proc->waitForFinished( -1 );
217 return 0;
218 }

mercurial