1 #include "pch.h"
2 #include "Replay.h"
3 #include "common/Def_Str.h"
4 #include <OgreTimer.h>
5 #include <string>
6 #include "CHud.h" //
7 using namespace std;
8 using namespace Ogre;
9
10
11 // header
12 //----------------------------------------------------------------
ReplayHeader2()13 ReplayHeader2::ReplayHeader2()
14 {
15 Default();
16 }
SetHead()17 void ReplayHeader2::SetHead()
18 {
19 head[0] = 'S'; head[1] = 'R'; head[2] = '/'; head[3] = '^'; head[4] = 0;
20 }
Default()21 void ReplayHeader2::Default()
22 {
23 SetHead();
24 track = ""; track_user = 0;
25 ver = 30; time = -0.01f;
26
27 numPlayers = 0; trees = 1.f;
28 num_laps = 1; networked = 0;
29 sim_mode = "";
30
31 cars.clear(); numWh.clear(); nicks.clear();
32 }
33
FromOld(const struct ReplayHeader & h)34 void ReplayHeader2::FromOld(const struct ReplayHeader& h)
35 {
36 time = -0.01f; //set later
37 ver = h.ver;
38 track = h.track;
39 track_user = h.track_user;
40
41 numPlayers = h.numPlayers;
42 cars.clear(); cars.resize(numPlayers);
43 numWh.clear(); numWh.resize(numPlayers);
44
45 cars[0] = h.car;
46 int i;
47 for (i=1; i < numPlayers; ++i)
48 cars[i] = h.cars[i-1];
49
50 for (i=0; i < numPlayers; ++i)
51 { string s = cars[i]; char w = 4;
52 if (s=="BV") w = 2; else // old, not 4 wheeled veh
53 if (s=="O"||s=="V1"||s=="V2"||s=="V3") w = 0;
54 numWh[i] = w;
55 }
56 trees = h.trees;
57 num_laps = h.num_laps;
58 networked = h.networked;
59 sim_mode = h.sim_mode;
60
61 nicks.clear();
62 if (networked)
63 { nicks.resize(numPlayers);
64 for (i=0; i < numPlayers; ++i)
65 nicks[i] = h.nicks[i];
66 }
67 }
68
ReplayFrame2()69 ReplayFrame2::ReplayFrame2()
70 :gear(0),fl(0) //..
71 {
72 }
73
74 /// convert old frame to new
75 //-------------------------------------------------------------------------------
FromOld(const ReplayFrame & f,uchar numWh,half prevHitTime)76 void ReplayFrame2::FromOld(const ReplayFrame& f, uchar numWh, half prevHitTime)
77 {
78 time = f.time; // save once..
79 pos = f.pos; rot = f.rot;
80
81 fl = 0; // zero flags
82 set(b_braking, f.braking);
83
84 // hud
85 gear = f.gear; rpm = f.rpm; vel = f.vel;
86 percent = f.percent /100.f*255.f; // track %
87 damage = 0.f; // wasnt saved
88
89 // sound, input
90 //LogO(String(" % ")+fToStr(f.percent)+" th "+fToStr(f.throttle)+" st "+fToStr(f.steer)+" b "+fToStr(f.fboost)+" c "+fToStr(f.clutch));
91 throttle = f.throttle *255.f; steer = f.steer *127.f;
92 fboost = f.fboost *255.f; clutch = f.clutch *255.f;
93 speed = f.speed; dynVel = f.dynVel;
94
95 hov_roll = f.hov_roll; //=sph_yaw for O
96 if (f.hov_roll != 0.f) set(b_hov, true);
97
98
99 // hit continuous ---
100 bool hasScr = f.fCarScrap > 0.f || f.fCarScreech > 0.f;
101 set(b_scrap, hasScr);
102 if (hasScr)
103 { RScrap scr;
104 scr.fScrap = half(f.fCarScrap); scr.fScreech = half(f.fCarScreech);
105 scrap.clear(); scrap.push_back(scr);
106 };
107
108 // new hit data impact
109 fHitTime = f.fHitTime;
110 bool h = fHitTime > prevHitTime; //== 1.f;
111 // wrong if saving every nth frame, what with higher force in skipped frames?..
112 set(b_hit, h);
113 if (h)
114 { RHit ht;
115 ht.fParIntens = f.fParIntens; ht.fParVel = f.fParVel;
116 ht.vHitPos = f.vHitPos; ht.vHitNorm = f.vHitNorm; // world
117 ht.fHitForce = f.fHitForce;
118 hit.push_back(ht);
119 };
120
121 // wheels ---
122 wheels.clear();
123 for (int i=0; i < numWh; ++i)
124 { RWheel wh;
125
126 wh.pos = f.whPos[i];
127 for (int q=0; q<4; ++q) wh.rot[q] = f.whRot[i][q];
128
129 // wheel trails, particles, snd
130 wh.surfType = f.surfType[i]; wh.whTerMtr = f.whTerMtr[i];
131 wh.whRoadMtr = f.whRoadMtr[i]; wh.whP = f.whP[i]; //particle type
132
133 wh.squeal = f.squeal[i]; wh.slide = f.slide[i]; wh.whVel = f.whVel[i];
134 wh.suspVel = f.suspVel[i]; wh.suspDisp = f.suspDisp[i];
135
136 // fluids
137 wh.whH = f.whH[i] / 255.f; // submerge
138 wh.whAngVel = f.whAngVel[i];
139 wh.whSteerAng = i >= 2 ? 0.f : f.whSteerAng[i];
140
141 wheels.push_back(wh);
142 };
143 }
144
145
Replay2()146 Replay2::Replay2()
147 :idLast(0)
148 {
149 Clear();
150 }
151
152 // Init once per game
InitHeader(const char * track,bool trk_user,bool bClear)153 void Replay2::InitHeader(const char* track, bool trk_user, bool bClear)
154 {
155 header.Default();
156 header.track = track; header.track_user = trk_user ? 1 : 0;
157 if (bClear)
158 Clear();
159 }
160
ClearCars()161 void Replay2::ClearCars()
162 {
163 int pp = header.numPlayers;
164 header.cars.clear(); header.cars.resize(pp);
165 header.nicks.clear(); header.nicks.resize(pp);
166 header.numWh.clear(); header.numWh.resize(pp);
167 }
168
Clear(bool time)169 void Replay2::Clear(bool time)
170 {
171 idLast = 0;
172 if (time)
173 header.time = -0.01f; // so new 0.0 counts
174
175 int p,pp = header.numPlayers;
176 frames.resize(pp);
177 for (p=0; p < pp; ++p)
178 { frames[p].clear();
179 frames[p].reserve(cDefSize);
180 }
181 }
182
183
184 /// Load
185 //-------------------------------------------------------------------------------------------------------
LoadFile(string file,bool onlyHdr)186 bool Replay2::LoadFile(string file, bool onlyHdr)
187 {
188 ifstream fi(file.c_str(), ios::binary | ios::in);
189 if (!fi) return false;
190
191 Ogre::Timer ti;
192 bool convert = false;
193
194 // header check
195 fi.read(header.head,5);
196 if (strcmp(header.head,"SR\\_")==0)
197 convert = true;
198 else
199 if (strcmp(header.head,"SR/^")!=0)
200 {
201 LogO(">- Load replay2: "+file+" Error: Unknown header");
202 return false;
203 }
204
205 if (convert)
206 {
207 LogO(">- Load replay2 convert: "+file);
208
209 // load old, convert
210 Replay r;
211 r.LoadFile(file, onlyHdr);
212 header.FromOld(r.header);
213
214 Clear(); // clear
215
216 if (onlyHdr)
217 { header.time = r.GetTimeLength(); return true; }
218
219 header.ver = r.header.ver + 10; // ver +10 after convert
220
221 // check, rare
222 int p,i,ii = r.GetNumFrames();
223 for (p=0; p < header.numPlayers; ++p)
224 { int si = r.frames[p].size()-1;
225 ii = std::min(ii, si);
226 }
227
228 for (p=0; p < header.numPlayers; ++p)
229 {
230 uchar wh = header.numWh[p];
231 half prevHitTime = half(0.f);
232
233 for (i=0; i < ii; ++i)
234 if (i%2==0) // half frames
235 {
236 ReplayFrame2 f2;
237 f2.FromOld(r.frames[p][i], wh, prevHitTime);
238 AddFrame(f2,p);
239 prevHitTime = f2.fHitTime;
240 } }
241 header.time = r.GetTimeLength();
242 }
243 else // load new
244 {
245 uchar l; int i,s; char buf[256];
246 #define rd(a) fi.read((char*)&a, sizeof(a))
247 #define rs(s) { fi.read((char*)&l, 1); if (l>0) fi.read(buf, l); buf[l]=0; s = buf; } //string
248 //TODO: endianness, swap 2bytes.. ENDIAN_SWAP_16
249
250 // header ------
251 ReplayHeader2& h = header;
252 rd(h.ver); rd(h.time);
253 rs(h.track) rd(h.track_user);
254 String ss = ">- Load replay2 ";
255 ss += h.track+" ";
256 ss += StrTime(h.time)+" ";
257
258 rd(h.numPlayers); s = h.numPlayers;
259 h.cars.clear(); h.cars.resize(s);
260 h.numWh.clear(); h.numWh.resize(s);
261 for (i=0; i < s; ++i)
262 { rs(h.cars[i]) ss += h.cars[i]+" "; } ss+=" ";
263 for (i=0; i < s; ++i)
264 { rd(h.numWh[i]); ss += toStr(h.numWh[i])+" "; } ss+=" ";
265
266 rd(h.trees); rd(h.num_laps);
267 rd(h.networked); rs(h.sim_mode)
268
269 h.nicks.clear(); h.nicks.resize(s);
270 if (h.networked)
271 for (i=0; i < s; ++i)
272 { rs(h.nicks[i]) ss += h.nicks[i]+" "; } ss+=" ";
273
274 #ifdef LOG_RPL
275 LogO(ss);
276 //if (!onlyHdr)
277 // LogO(">- Load replay2: "+file+" players:"+toStr(h.numPlayers));
278 #endif
279
280 Clear(false); // clear
281
282 if (onlyHdr){ fi.close(); return true; }
283
284 // frames ------
285 i=0; int p,w; float prevTime = -1.f;
286 while (!fi.eof())
287 {
288 float time; rd(time); // once
289 for (p=0; p < header.numPlayers; ++p)
290 {
291 ReplayFrame2 f;
292 f.time = time;
293 // car
294 rd(f.pos); rd(f.rot); rd(f.fl); //b_braking etc
295 // hud
296 rd(f.gear); rd(f.rpm); rd(f.vel);
297 rd(f.damage); rd(f.clutch); rd(f.percent);
298 // sound, input
299 rd(f.throttle); rd(f.steer); rd(f.fboost);
300 rd(f.speed); rd(f.dynVel);
301 // ext
302 if (f.get(b_hov)) rd(f.hov_roll);
303 bool flu = f.get(b_fluid);
304 if (flu) rd(f.whMudSpin);
305
306 // wheels
307 int ww = header.numWh[p];
308 for (w=0; w < ww; ++w)
309 {
310 RWheel wh;
311 rd(wh.pos); rd(wh.rot);
312 // trl, par, snd
313 rd(wh.surfType); rd(wh.whTerMtr);
314 rd(wh.whRoadMtr); rd(wh.whP);
315 // tire
316 rd(wh.squeal); rd(wh.slide); rd(wh.whVel);
317 rd(wh.suspVel); rd(wh.suspDisp);
318 // fluids
319 if (flu) rd(wh.whH);
320 rd(wh.whAngVel); rd(wh.whSteerAng);
321 f.wheels.push_back(wh);
322 }
323
324 // hit data
325 if (f.get(b_scrap))
326 {
327 RScrap s;
328 rd(s.fScrap); rd(s.fScreech);
329 f.scrap.push_back(s);
330 }
331 rd(f.fHitTime);
332 if (f.get(b_hit))
333 {
334 RHit h;
335 rd(h.fHitForce); rd(h.fParIntens); rd(h.fParVel);
336 rd(h.vHitPos.x); rd(h.vHitPos.y); rd(h.vHitPos.z);
337 rd(h.vHitNorm.x); rd(h.vHitNorm.y); rd(h.vHitNorm.z);
338 f.hit.push_back(h);
339 }
340
341 if (time <= prevTime)
342 {
343 #ifdef LOG_RPL
344 LogO(">- Load replay2 =time id:"+toStr(i)+" plr:"+toStr(p)+" t-1:"+fToStr(prevTime,5,7)+" => t:"+fToStr(time,5,7));
345 #endif
346 }else
347 if (!fi.eof())
348 frames[p].push_back(f);
349 }
350 ++i; prevTime = time;
351 }
352 fi.close();
353 }
354
355 #ifdef LOG_RPL
356 if (frames.empty() || frames[0].empty())
357 LogO(">- Load replay2 empty!! time: "+fToStr(GetTimeLength(),2,5));
358 else
359 LogO(">- Load replay2 plr: "+toStr(header.numPlayers)+" t1st: "+fToStr(frames[0][0].time,5,7)+
360 " time: "+fToStr(GetTimeLength(),2,5)+" frames: "+toStr(frames[0].size()));
361 #endif
362
363 LogO(String("::: Time Replay2 Load: ") + fToStr(ti.getMilliseconds(),0,3) + " ms");
364 return true;
365 }
366
367 /// Save
368 //-------------------------------------------------------------------------------------------------------
SaveFile(string file)369 bool Replay2::SaveFile(string file)
370 {
371 ofstream of(file.c_str(), ios::binary | ios::out);
372 if (!of) return false;
373
374 uchar l; int i,s;
375 #define ws(s) { l = s.length(); of.write((char*)&l, 1); of.write(s.c_str(), l); } //string
376 #define wr(a) of.write((char*)&a, sizeof(a))
377 //todo: portability, endianness for shorts?..
378
379 // header ------
380 const ReplayHeader2& h = header;
381 header.SetHead();
382 of.write((char*)&h.head, 5);
383 wr(h.ver); wr(h.time);
384 ws(h.track) wr(h.track_user);
385
386 wr(h.numPlayers);
387 s = h.numPlayers; // car names
388 for (i=0; i < s; ++i) ws(h.cars[i])
389 for (i=0; i < s; ++i) wr(h.numWh[i]);
390
391 wr(h.trees); wr(h.num_laps);
392 wr(h.networked); ws(h.sim_mode)
393
394 if (h.networked)
395 for (i=0; i < s; ++i) ws(h.nicks[i])
396
397
398 // frames ------
399 s = frames[0].size(); int p,w;
400 //s = 1; p = 1; //test
401
402 for (i=0; i < s; ++i)
403 {
404 float time = frames[0][i].time;
405 wr(time); // once
406 for (p=0; p < header.numPlayers; ++p)
407 {
408 ReplayFrame2 f = frames[p][i];
409 // car
410 wr(f.pos); wr(f.rot); wr(f.fl); //b_braking etc
411 // hud
412 wr(f.gear); wr(f.rpm); wr(f.vel);
413 wr(f.damage); wr(f.clutch); wr(f.percent);
414 // sound, input
415 wr(f.throttle); wr(f.steer); wr(f.fboost);
416 wr(f.speed); wr(f.dynVel);
417 // ext
418 if (f.get(b_hov)) wr(f.hov_roll);
419 bool flu = f.get(b_fluid);
420 if (flu) wr(f.whMudSpin);
421
422 // wheels
423 int ww = f.wheels.size();
424 for (w=0; w < ww; ++w)
425 {
426 const RWheel& wh = f.wheels[w];
427 wr(wh.pos); wr(wh.rot);
428 // trl, par, snd
429 wr(wh.surfType); wr(wh.whTerMtr);
430 wr(wh.whRoadMtr); wr(wh.whP);
431 // tire
432 wr(wh.squeal); wr(wh.slide); wr(wh.whVel);
433 wr(wh.suspVel); wr(wh.suspDisp);
434 // fluids
435 if (flu) wr(wh.whH);
436 wr(wh.whAngVel); wr(wh.whSteerAng);
437 }
438
439 // hit data
440 if (f.get(b_scrap) /*&& scrap.size()==1*/)
441 {
442 const RScrap& s = f.scrap[0];
443 wr(s.fScrap); wr(s.fScreech);
444 }
445 wr(f.fHitTime);
446 if (f.get(b_hit) /*&& hit.size()==1*/)
447 {
448 const RHit& h = f.hit[0];
449 wr(h.fHitForce); wr(h.fParIntens); wr(h.fParVel);
450 wr(h.vHitPos.x); wr(h.vHitPos.y); wr(h.vHitPos.z);
451 wr(h.vHitNorm.x); wr(h.vHitNorm.y); wr(h.vHitNorm.z);
452 }
453 }
454 }
455
456 of.close();
457 return true;
458 }
459 //-------------------------------------------------------------------------------------------------------
460
461
462 // add (Record)
AddFrame(const ReplayFrame2 & frame,int carNum)463 void Replay2::AddFrame(const ReplayFrame2& frame, int carNum)
464 {
465 if (carNum > 0 || frame.time > GetTimeLength()) // dont add before last
466 {
467 frames[carNum].push_back(frame);
468 header.time = frame.time;
469 }
470 }
471
472 // CopyFrom
CopyFrom(const Replay2 & rpl)473 void Replay2::CopyFrom(const Replay2& rpl)
474 {
475 header.numPlayers = rpl.header.numPlayers;
476 Clear();
477 header.time = rpl.header.time;
478
479 // plr 1 only, for ghost
480 for (int i=0; i < rpl.GetNumFrames(); ++i)
481 frames[0].push_back(rpl.frames[0][i]);
482 }
483
484 // last frame time, sec
GetTimeLength() const485 const float Replay2::GetTimeLength() const
486 {
487 return header.time;
488 }
489
490 // get Last
GetLastFrame(ReplayFrame2 * pFr,int carNum)491 bool Replay2::GetLastFrame(ReplayFrame2* pFr, int carNum)
492 {
493 int s = frames[carNum].size();
494 if (s < 2) return false; // empty
495
496 *pFr = frames[carNum][s-1];
497 return false;
498 }
499
GetLastHitTime(int carNum)500 half Replay2::GetLastHitTime(int carNum)
501 {
502 int s = frames[carNum].size();
503 if (s < 2) return half(0.f); // empty
504
505 return frames[carNum][s-1].fHitTime;
506 }
507
508
509 /// get (Play)
510 //----------------------------------------------------------------
GetFrame(float time1,ReplayFrame2 * pFr,int carNum)511 bool Replay2::GetFrame(float time1, ReplayFrame2* pFr, int carNum)
512 {
513 if (frames.empty()) return false;
514 int& ic = idLast; // last index
515
516 int s = frames[carNum].size();
517 if (ic > s-1) ic = s-1; // new size
518 if (s < 2) return false; // empty
519
520 /// find which frame for given time
521 float time = std::min(time1, GetTimeLength());
522 while (ic+1 < s-1 && frames[carNum][ic+1].time <= time) ++ic;
523 while (ic > 0 && frames[carNum][ic].time > time) --ic;
524
525 if (ic < 0 || ic >= s)
526 return false; //-
527
528
529 if (ic == 0 || ic == s-1)
530 *pFr = frames[carNum][ic];
531 else
532 { /// linear interpolation
533 const ReplayFrame2& t1 = frames[carNum][ic]; //cur
534 const ReplayFrame2& t0 = frames[carNum][std::max(0, ic-1)]; //prev
535 *pFr = frames[carNum][ic]; // rest, no interp
536
537 float dt = t1.time - t0.time;
538 if (dt > 0.0001f)
539 {
540 float f = (time - t0.time) / dt;
541 (*pFr).pos = t0.pos + (t1.pos - t0.pos) * f;
542 (*pFr).rot = t0.rot.QuatSlerp(t1.rot, f);
543
544 int w, ww = t0.wheels.size();
545 for (w=0; w < ww; ++w)
546 {
547 (*pFr).wheels[w].pos = t0.wheels[w].pos + (t1.wheels[w].pos - t0.wheels[w].pos) * f;
548 //(*pFr).wheels[w].rot = t0.wheels[w].rot.QuatSlerp(t1.wheels[w].rot, f); // no need
549 }
550 } }
551
552 // last time
553 float end = GetTimeLength();
554 if (time1 >= end)
555 {
556 pFr->fboost = 0.f;
557 // clear emitters at end..
558 int w, ww = (*pFr).wheels.size();
559 for (w=0; w < ww; ++w)
560 {
561 RWheel& wh = (*pFr).wheels[w];
562 wh.slide = wh.squeal = wh.whVel = half(0.f);
563 }
564 }
565
566 // check if ended
567 return time1 <= end;
568 }
569
570 // delete frames after current time (when time did go back)
DeleteFrames(int c,float fromTime)571 void Replay2::DeleteFrames(int c, float fromTime)
572 {
573 if (frames[c].empty()) return;
574 while (!frames[c].empty() && frames[c][ frames[c].size()-1 ].time >= fromTime)
575 frames[c].pop_back();
576 header.time = fromTime;
577 }
578
579