src/demo.cpp

changeset 10
bc1414343e19
parent 8
e8f645d9f28f
child 11
3ddebf76105e
equal deleted inserted replaced
9:f9893eea978b 10:bc1414343e19
4 #include <QProcess> 4 #include <QProcess>
5 #include "demo.h" 5 #include "demo.h"
6 #include "bytestream.h" 6 #include "bytestream.h"
7 #include "misc.h" 7 #include "misc.h"
8 #include "ui_demoprompt.h" 8 #include "ui_demoprompt.h"
9 9 #include "prompts.h"
10 static const uint32 g_demoSignature = makeByteID( 'Z', 'C', 'L', 'D' ); 10
11 11 static const uint32 g_demoSignature = makeByteID ('Z', 'C', 'L', 'D');
12 // ============================================================================= 12
13 // ----------------------------------------------------------------------------- 13 // =============================================================================
14 static str tr( const char* msg ) { 14 // -----------------------------------------------------------------------------
15 return QObject::tr( msg ); 15 str uncolorize (const str& in) {
16 } 16 str out;
17 17 int skip = 0;
18 // ============================================================================= 18
19 // ----------------------------------------------------------------------------- 19 for (const qchar& c : in) {
20 static void error( str msg ) { 20 if (skip-- > 0)
21 QMessageBox::critical( null, "Error", msg ); 21 continue;
22 } 22
23 23 if (c.toAscii() == '\034') {
24 // ============================================================================= 24 skip = 1;
25 // ----------------------------------------------------------------------------- 25 continue;
26 static int getVersionIndex( str ver ) { 26 }
27 int i; 27
28 28 out += c;
29 for( i = 0; i < g_zanVersions.size(); ++i ) 29 }
30 if( g_zanVersions[i] == ver ) 30
31 return i; 31 return out;
32 32 }
33 return -1; 33
34 } 34 // =============================================================================
35 35 // -----------------------------------------------------------------------------
36 // ============================================================================= 36 static str tr (const char* msg) {
37 // ----------------------------------------------------------------------------- 37 return QObject::tr (msg);
38 static str findWAD( str name ) { 38 }
39
40 // =============================================================================
41 // -----------------------------------------------------------------------------
42 static void error (str msg) {
43 QMessageBox::critical (null, "Error", msg);
44 }
45
46 // =============================================================================
47 // -----------------------------------------------------------------------------
48 static bool isKnownVersion (str ver) {
39 QSettings cfg; 49 QSettings cfg;
40 list<var> paths = cfg.value( "wads/paths", list<var>() ).toList(); 50 list<var> versions = getVersionsList();
41 51
42 if( paths.size() == 0 ) { 52 for (const var& it : versions)
43 error( tr( "No WAD paths configured!" )); 53 if (it.toString() == ver || it.toString() + 'M' == ver)
44 exit( 9 ); 54 return true;
45 } 55
46 56 return false;
47 for( var it : paths ) { 57 }
48 str fullpath = fmt( "%1/%2", it.toString(), name ); 58
49 QFile f( fullpath ); 59 // =============================================================================
50 60 // -----------------------------------------------------------------------------
51 if( f.exists() ) 61 static str findWAD (str name) {
62 QSettings cfg;
63 list<var> paths = cfg.value ("wads/paths", list<var>()).toList();
64
65 if (paths.size() == 0) {
66 error (tr ("No WAD paths configured!"));
67
68 // Cannot just return an empty string here since that'd trigger
69 // another error prompt - skip ahead and exit.
70 exit (9);
71 }
72
73 for (const var& it : paths) {
74 str fullpath = fmt ("%1/%2", it.toString(), name);
75 QFile f (fullpath);
76
77 if (f.exists())
52 return fullpath; 78 return fullpath;
53 } 79 }
54 80
55 return ""; 81 return "";
56 } 82 }
57 83
58 // ============================================================================= 84 // =============================================================================
59 // ----------------------------------------------------------------------------- 85 // -----------------------------------------------------------------------------
60 int launchDemo( str path ) { 86 int launchDemo (str path) {
61 FILE* fp = fopen( path.toStdString().c_str(), "r" ); 87 FILE* fp = fopen (path.toStdString().c_str(), "r");
62 88
63 if( !fp ) { 89 if (!fp) {
64 error( fmt( tr( "Couldn't open '%1' for reading: %2" ), path, strerror( errno ))); 90 error (fmt (tr ("Couldn't open '%1' for reading: %2"), path, strerror (errno)));
65 return 1; 91 return 1;
66 } 92 }
67 93
68 fseek( fp, 0, SEEK_END ); 94 fseek (fp, 0, SEEK_END);
69 const size_t fsize = ftell( fp ); 95 const size_t fsize = ftell (fp);
70 rewind( fp ); 96 rewind (fp);
71 97
72 char* buf = new char[fsize]; 98 char* buf = new char[fsize];
73 99
74 if( fread( buf, 1, fsize, fp ) != fsize ) { 100 if (fread (buf, 1, fsize, fp) != fsize) {
75 error( tr( "I/O error" )); 101 error (tr ("I/O error"));
76 delete[] buf; 102 delete[] buf;
77 return 2; 103 return 2;
78 } 104 }
79 105
80 Bytestream s( buf, fsize ); 106 Bytestream s (buf, fsize);
81 delete[] buf; 107 delete[] buf;
82 108
83 uint8 offset; 109 uint8 offset;
84 uint32 length; 110 uint32 length;
85 111
91 117
92 // Check signature 118 // Check signature
93 { 119 {
94 uint32 sig; 120 uint32 sig;
95 121
96 if( !s.readLong( sig ) || sig != g_demoSignature ) { 122 if (!s.readLong (sig) || sig != g_demoSignature) {
97 error( fmt( tr( "'%1' is not a Zandronum demo file!" ), path )); 123 error (fmt (tr ("'%1' is not a Zandronum demo file!"), path));
98 return 3; 124 return 3;
99 } 125 }
100 } 126 }
101 127
102 // Zandronum stores CLD_DEMOLENGTH after the signature. This is also the 128 // Zandronum stores CLD_DEMOLENGTH after the signature. This is also the
103 // first demo enumerator, so we can determine the offset (which is variable!) 129 // first demo enumerator, so we can determine the offset (which is variable!)
104 // from this byte 130 // from this byte
105 s.readByte( offset ); 131 s.readByte (offset);
106 s.readLong( length ); 132 s.readLong (length);
107 133
108 uint16 zanversionID, numWads; 134 uint16 zanversionID, numWads;
109 uint32 longSink; 135 uint32 longSink;
110 str zanversion; 136 str zanversion;
111 list<str> wads; 137 list<str> wads;
112 bool ready = false; 138 bool ready = false;
113 139 uint8 buildID;
114 for( ;; ) { 140
141 // Read the demo header and get data
142 for (;;) {
115 uint8 header; 143 uint8 header;
116 if( !s.readByte( header )) 144
145 if (!s.readByte (header))
117 break; 146 break;
118 147
119 if( header == DemoBodyStart + offset ) { 148 if (header == DemoBodyStart + offset) {
120 ready = true; 149 ready = true;
121 break; 150 break;
122 } elif( header == DemoVersion + offset ) { 151 } elif (header == DemoVersion + offset) {
123 s.readShort( zanversionID ); 152 s.readShort (zanversionID);
124 s.readString( zanversion ); 153 s.readString (zanversion);
125 s.readLong( longSink ); // rng seed - we don't need it 154
126 } elif( header == DemoUserInfo + offset ) { 155 if (zanversion.left (4) != "1.1-" && zanversion.left (6) != "1.1.1-")
127 s.readString( userinfo.netname ); 156 s.readByte (buildID);
128 s.readByte( userinfo.gender ); 157 else
129 s.readLong( userinfo.color ); 158 buildID = 1;
130 s.readLong( userinfo.aimdist ); 159
131 s.readString( userinfo.skin ); 160 s.readLong (longSink); // rng seed - we don't need it
132 s.readLong( userinfo.railcolor ); 161 } elif (header == DemoUserInfo + offset) {
133 s.readByte( userinfo.handicap ); 162 s.readString (userinfo.netname);
134 s.readByte( userinfo.unlagged ); 163 s.readByte (userinfo.gender);
135 s.readByte( userinfo.respawnOnFire ); 164 s.readLong (userinfo.color);
136 s.readByte( userinfo.ticsPerUpdate ); 165 s.readLong (userinfo.aimdist);
137 s.readByte( userinfo.connectionType ); 166 s.readString (userinfo.skin);
138 s.readString( userinfo.className ); 167 s.readLong (userinfo.railcolor);
139 } elif( header == DemoWads + offset ) { 168 s.readByte (userinfo.handicap);
140 s.readShort( numWads ); 169 s.readByte (userinfo.unlagged);
141 170 s.readByte (userinfo.respawnOnFire);
142 for( uint8 i = 0; i < numWads; ++i ) { 171 s.readByte (userinfo.ticsPerUpdate);
172 s.readByte (userinfo.connectionType);
173 s.readString (userinfo.className);
174 } elif (header == DemoWads + offset) {
175 str sink;
176 s.readShort (numWads);
177
178 for (uint8 i = 0; i < numWads; ++i) {
143 str wad; 179 str wad;
144 s.readString( wad ); 180 s.readString (wad);
145 wads << wad; 181 wads << wad;
146 } 182 }
147 183
148 // The demo has two checksum strings. We're not interested 184 // The demo has two checksum strings. We're not interested
149 // in them though. 185 // in them though.
150 str sink; 186 for (int i = 0; i < 2; ++i)
151 for( int i = 0; i < 2; ++i ) 187 s.readString (sink);
152 s.readString( sink );
153 } else { 188 } else {
154 error( fmt( tr( "Unknown header %1!\n" ), (int) header )); 189 error (fmt (tr ("Unknown header %1!\n"), (int) header));
155 return 4; 190 return 3;
156 } 191 }
157 } 192 }
158 193
159 if( !ready ) { 194 if (!ready) {
160 error( fmt( tr( "Incomplete demo header in '%s'!" ), path )); 195 error (fmt (tr ("Incomplete demo header in '%s'!"), path));
161 return 5; 196 return 3;
162 } 197 }
163 198
164 int i = getVersionIndex( zanversion ); 199 if (!isKnownVersion (zanversion)) {
165 if( i == -1 ) { 200 UnknownVersionPrompt* prompt = new UnknownVersionPrompt (path, zanversion, (buildID == 1));
166 error( fmt( tr( "Unknown Zandronum version %1!\n" ), zanversion )); 201 if (!prompt->exec())
167 return 6; 202 return 6;
168 } 203
169 204 if (!isKnownVersion (zanversion)) {
205 error (tr ("Failure in configuration! This shouldn't happen."));
206 return 6;
207 }
208 }
209
170 QSettings cfg; 210 QSettings cfg;
171 str binarypath = cfg.value( binaryConfigName( zanversion )).toString(); 211 str binarypath = cfg.value (binaryConfigName (zanversion)).toString();
172 212
173 if( binarypath.isEmpty() ) { 213 if (binarypath.isEmpty()) {
174 error( fmt( tr( "No binary path specified for Zandronum version %1!\n" ), zanversion )); 214 error (fmt (tr ("No binary path specified for Zandronum version %1!"), zanversion));
175 return 7; 215 return 7;
176 } 216 }
177 217
178 str iwad, iwadpath; 218 str iwadpath;
179 list<str> pwads, pwadpaths; 219 list<str> pwadpaths;
180 220
181 // Find the WADs 221 // Find the WADs
182 for( const str& wad : wads ) { 222 for (const str& wad : wads) {
183 str path = findWAD( wad ); 223 str path = findWAD (wad);
184 224
185 // IWAD names can appear in uppercase too. Linux is case-sensitive, so.. 225 // IWAD names can appear in uppercase too. Linux is case-sensitive, so..
186 if( &wad == &wads[0] && path.isEmpty() ) 226 if (&wad == &wads[0] && path.isEmpty())
187 path = findWAD( wad.toUpper() ); 227 path = findWAD (wad.toUpper());
188 228
189 if( path.isEmpty() ) { 229 if (path.isEmpty()) {
190 error( fmt( tr( "Couldn't find %1!\n" ), wad )); 230 error (fmt (tr ("Couldn't find %1!"), wad));
191 return 8; 231 return 8;
192 } 232 }
193 233
194 if( &wad == &wads[0] ) { 234 if (&wad == &wads[0])
195 iwadpath = path; 235 iwadpath = path;
196 iwad = wad; 236 else
197 } else {
198 pwadpaths << path; 237 pwadpaths << path;
199 pwads << wad; 238 }
200 } 239
201 } 240 if (!cfg.value ("nodemoprompt", false).toBool()) {
202
203 if( !cfg.value( "nodemoprompt", false ).toBool() ) {
204 str pwadtext; 241 str pwadtext;
205 for( const str& pwad : pwads ) { 242
206 if( !pwadtext.isEmpty() ) 243 for (const str& pwad : wads) {
244 if (&pwad == &wads[0])
245 continue; // skip the IWAD
246
247 if (!pwadtext.isEmpty())
207 pwadtext += "<br />"; 248 pwadtext += "<br />";
208 249
209 pwadtext += pwad; 250 pwadtext += pwad;
210 } 251 }
211 252
212 QDialog* dlg = new QDialog; 253 QDialog* dlg = new QDialog;
213 Ui_DemoPrompt ui; 254 Ui_DemoPrompt ui;
214 ui.setupUi( dlg ); 255 ui.setupUi (dlg);
215 ui.demoNameLabel->setText( basename( path )); 256 ui.demoNameLabel->setText (basename (path));
216 ui.demoRecorder->setText( userinfo.netname ); 257 ui.demoRecorder->setText (uncolorize (userinfo.netname));
217 ui.versionLabel->setText( zanversion ); 258 ui.versionLabel->setText (zanversion);
218 ui.iwadLabel->setText( wads[0] ); 259 ui.iwadLabel->setText (wads[0]);
219 ui.pwadsLabel->setText( pwadtext ); 260 ui.pwadsLabel->setText (pwadtext);
220 261 dlg->setWindowTitle (fmt (APPNAME " %1", versionString()));
221 if( !dlg->exec() ) 262
263 if (!dlg->exec())
222 return 1; 264 return 1;
223 } 265 }
224 266
225 print( "binary: %1\n", binarypath ); 267 QStringList cmdlineList ( {
226 print( "iwad: %1\npwads: %2\n", iwadpath, pwadpaths );
227
228 QStringList cmdlineList ({
229 "-playdemo", path, 268 "-playdemo", path,
230 "-iwad", iwadpath, 269 "-iwad", iwadpath,
231 }); 270 });
232 271
233 if( pwadpaths.size() > 0 ) { 272 if (pwadpaths.size() > 0) {
234 cmdlineList << "-file"; 273 cmdlineList << "-file";
235 cmdlineList << pwadpaths; 274 cmdlineList << pwadpaths;
236 } 275 }
237 276
238 print( "commandline: %1 %2\n", binarypath, cmdlineList.join( " " )); 277 print ("Executing: %1 %2\n", binarypath, cmdlineList.join (" "));
239 QProcess* proc = new QProcess; 278 QProcess* proc = new QProcess;
240 proc->start( binarypath, cmdlineList ); 279 proc->start (binarypath, cmdlineList);
241 proc->waitForFinished( -1 ); 280 proc->waitForFinished (-1);
242 return 0; 281 return 0;
243 } 282 }

mercurial