99 return ""; |
98 return ""; |
100 } |
99 } |
101 |
100 |
102 // ============================================================================= |
101 // ============================================================================= |
103 // ----------------------------------------------------------------------------- |
102 // ----------------------------------------------------------------------------- |
104 #ifdef _WIN32 |
103 QDataStream& operator>> (QDataStream& stream, str& out) { |
105 # define FILE_FLAGS "rb" |
104 uint8 c; |
106 #else |
105 out = ""; |
107 # define FILE_FLAGS "r" |
106 |
108 #endif // _WIN32 |
107 for (stream >> c; c != '\0'; stream >> c) |
109 |
108 out += c; |
|
109 |
|
110 return stream; |
|
111 } |
|
112 |
|
113 // ============================================================================= |
|
114 // ----------------------------------------------------------------------------- |
110 int launchDemo (str path) { |
115 int launchDemo (str path) { |
111 FILE* fp = fopen (path.toStdString().c_str(), FILE_FLAGS); |
116 QFile f (path); |
112 |
117 |
113 if (!fp) { |
118 if (!f.open (QIODevice::ReadOnly)) { |
114 error (fmt (tr ("Couldn't open '%1' for reading: %2"), path, strerror (errno))); |
119 error (fmt (tr ("Couldn't open '%1' for reading: %2"), path, strerror (errno))); |
115 return 1; |
120 return 1; |
116 } |
121 } |
117 |
122 |
118 fseek (fp, 0, SEEK_END); |
123 QDataStream stream (&f); |
119 const size_t fsize = ftell (fp); |
124 stream.setByteOrder (QDataStream::LittleEndian); |
120 rewind (fp); |
|
121 |
|
122 char* buf = new char[fsize]; |
|
123 |
|
124 const size_t bytesRead = fread (buf, 1, fsize, fp); |
|
125 if (bytesRead != fsize) { |
|
126 error (fmt (tr ("I/O error: %1 / %2 bytes read"), bytesRead, fsize)); |
|
127 delete[] buf; |
|
128 return 2; |
|
129 } |
|
130 |
|
131 Bytestream s (buf, fsize); |
|
132 delete[] buf; |
|
133 |
125 |
134 uint8 offset; |
126 uint8 offset; |
135 uint32 length; |
127 uint32 length; |
|
128 uint16 zanversionID, numWads; |
|
129 uint32 longSink; |
|
130 str zanversion; |
|
131 list<str> wads; |
|
132 uint8 buildID; |
|
133 bool ready = false; |
136 |
134 |
137 struct { |
135 struct { |
138 str netname, skin, className; |
136 str netname, skin, className; |
139 uint8 gender, handicap, unlagged, respawnOnFire, ticsPerUpdate, connectionType; |
137 uint8 gender, handicap, unlagged, respawnOnFire, ticsPerUpdate, connectionType; |
140 uint32 color, aimdist, railcolor; |
138 uint32 color, aimdist, railcolor; |
142 |
140 |
143 // Check signature |
141 // Check signature |
144 { |
142 { |
145 uint32 sig; |
143 uint32 sig; |
146 |
144 |
147 if (!s.readLong (sig) || sig != g_demoSignature) { |
145 stream >> sig; |
|
146 if (sig != g_demoSignature) { |
148 error (fmt (tr ("'%1' is not a Zandronum demo file!"), path)); |
147 error (fmt (tr ("'%1' is not a Zandronum demo file!"), path)); |
149 return 3; |
148 return 3; |
150 } |
149 } |
151 } |
150 } |
152 |
151 |
153 // Zandronum stores CLD_DEMOLENGTH after the signature. This is also the |
152 // Zandronum stores CLD_DEMOLENGTH after the signature. This is also the |
154 // first demo enumerator, so we can determine the offset (which is variable!) |
153 // first demo enumerator, so we can determine the offset (which is variable!) |
155 // from this byte |
154 // from this byte |
156 s.readByte (offset); |
155 stream >> offset |
157 s.readLong (length); |
156 >> length; |
158 |
|
159 uint16 zanversionID, numWads; |
|
160 uint32 longSink; |
|
161 str zanversion; |
|
162 list<str> wads; |
|
163 bool ready = false; |
|
164 uint8 buildID; |
|
165 |
157 |
166 // Read the demo header and get data |
158 // Read the demo header and get data |
167 for (;;) { |
159 for (;;) { |
168 uint8 header; |
160 uint8 header; |
169 |
161 stream >> header; |
170 if (!s.readByte (header)) |
162 print ("header: %1\n", (int) header); |
171 break; |
|
172 |
163 |
173 if (header == DemoBodyStart + offset) { |
164 if (header == DemoBodyStart + offset) { |
174 ready = true; |
165 ready = true; |
175 break; |
166 break; |
176 } elif (header == DemoVersion + offset) { |
167 } elif (header == DemoVersion + offset) { |
177 s.readShort (zanversionID); |
168 print ("Read demo version\n"); |
178 s.readString (zanversion); |
169 stream >> zanversionID |
|
170 >> zanversion; |
|
171 |
|
172 print ("version ID: %1, version: %2\n", zanversionID, zanversion); |
179 |
173 |
180 if (zanversion.left (4) != "1.1-" && zanversion.left (6) != "1.1.1-") |
174 if (zanversion.left (4) != "1.1-" && zanversion.left (6) != "1.1.1-") |
181 s.readByte (buildID); |
175 stream >> buildID; |
182 else |
176 else { |
|
177 // Assume a release build if not supplied. The demo only got the |
|
178 // "ZCLD" signature in the 1.1 release build, 1.1.1 had no |
|
179 // development binaries and the build ID will be included in 1.1.2. |
183 buildID = 1; |
180 buildID = 1; |
184 |
181 } |
185 s.readLong (longSink); // rng seed - we don't need it |
182 |
|
183 stream >> longSink; // rng seed - we don't need it |
186 } elif (header == DemoUserInfo + offset) { |
184 } elif (header == DemoUserInfo + offset) { |
187 s.readString (userinfo.netname); |
185 print ("Read userinfo\n"); |
188 s.readByte (userinfo.gender); |
186 stream >> userinfo.netname |
189 s.readLong (userinfo.color); |
187 >> userinfo.gender |
190 s.readLong (userinfo.aimdist); |
188 >> userinfo.color |
191 s.readString (userinfo.skin); |
189 >> userinfo.aimdist |
192 s.readLong (userinfo.railcolor); |
190 >> userinfo.skin |
193 s.readByte (userinfo.handicap); |
191 >> userinfo.railcolor |
194 s.readByte (userinfo.unlagged); |
192 >> userinfo.handicap |
195 s.readByte (userinfo.respawnOnFire); |
193 >> userinfo.unlagged |
196 s.readByte (userinfo.ticsPerUpdate); |
194 >> userinfo.respawnOnFire |
197 s.readByte (userinfo.connectionType); |
195 >> userinfo.ticsPerUpdate |
198 s.readString (userinfo.className); |
196 >> userinfo.connectionType |
|
197 >> userinfo.className; |
199 } elif (header == DemoWads + offset) { |
198 } elif (header == DemoWads + offset) { |
200 str sink; |
199 str sink; |
201 s.readShort (numWads); |
200 stream >> numWads; |
202 |
201 |
203 for (uint8 i = 0; i < numWads; ++i) { |
202 for (uint8 i = 0; i < numWads; ++i) { |
204 str wad; |
203 str wad; |
205 s.readString (wad); |
204 stream >> wad; |
206 wads << wad; |
205 wads << wad; |
207 } |
206 } |
208 |
207 |
209 // The demo has two checksum strings. We're not interested |
208 // The demo has two checksum strings. We're not interested |
210 // in them though. |
209 // in them though. Down the sink they go... |
211 for (int i = 0; i < 2; ++i) |
210 for (int i = 0; i < 2; ++i) |
212 s.readString (sink); |
211 stream >> sink; |
213 } else { |
212 } else { |
214 error (fmt (tr ("Unknown header %1!\n"), (int) header)); |
213 error (fmt (tr ("Unknown header %1!\n"), (int) header)); |
215 return 3; |
214 return 3; |
216 } |
215 } |
217 } |
216 } |