1 #include "pch.h"
2 #include "common/Def_Str.h"
3 #include "common/Gui_Def.h"
4 #include "common/GuiCom.h"
5 #include "../vdrift/pathmanager.h"
6 #include "../vdrift/game.h"
7 #include "CGame.h"
8 #include "CHud.h"
9 #include "CGui.h"
10 #include "FollowCamera.h"
11 #include <time.h>
12 #include <boost/filesystem.hpp>
13 using namespace std;
14 using namespace Ogre;
15 using namespace MyGUI;
16 namespace fs = boost::filesystem;
17 
18 
19 ///  [Replay]  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20 //------------------------------------------------------------------------------------------------------------------
21 
slRplPosEv(SL)22 void CGui::slRplPosEv(SL)  // change play pos
23 {
24 	if (!app->bRplPlay)  return;
25 	double oldt = pGame->timer.GetReplayTime(0);
26 	double v = val;  v = std::max(0.0, std::min(1.0, v));  v *= app->replay.GetTimeLength();
27 	pGame->timer.SetReplayTime(0, v);
28 
29 	FollowCamera* fCam = (*app->carModels.begin())->fCam;
30 	fCam->First();  // instant change
31 	//for (int i=0; i < 10; ++i)
32 	//	fCam->update(fabs(v-oldt)/10.f, 0);  //..?
33 }
34 
getRplName()35 String CGui::getRplName()
36 {
37 	String name;
38 	int i = rplList->getIndexSelected(), p;  if (i == ITEM_NONE)  return name;
39 	name = rplList->getItemNameAt(i);
40 	if (name.length() > 7 && name[0]=='#')  name = name.substr(7);
41 	return name;
42 }
43 
44 
btnRplLoad(WP)45 void CGui::btnRplLoad(WP)  // Load
46 {
47 	//  from list
48 	String name = getRplName();  if (name.empty())  return;
49 	string file = GetRplListDir() + "/" + name + ".rpl";
50 
51 	if (!app->replay.LoadFile(file))
52 	{
53 		Message::createMessageBox(
54 			"Message", TR("#{Replay} - #{RplLoad}"), TR("#{Error}."),
55 			MessageBoxStyle::IconWarning | MessageBoxStyle::Ok);
56 	}else
57 	{	//  car, track change
58 		const ReplayHeader2& h = app->replay.header;
59 		string trk = h.track;
60 		bool usr = h.track_user == 1;
61 
62 		//  check if cars, track exist
63 		String er;  int p;
64 		if (!h.track_user && !fs::exists(PATHMANAGER::Tracks()+"/"+trk))
65 			er += TR("#{Track}: ")+trk+TR(" - #{DoesntExist}.\n");
66 		if (h.track_user && !fs::exists(PATHMANAGER::TracksUser()+"/"+trk))
67 			er += TR("#{Track} (#{TweakUser}): ")+trk+TR(" - #{DoesntExist}.\n");
68 
69 		for (p=0; p < h.numPlayers; ++p)
70 			if (!fs::exists(PATHMANAGER::Cars()+"/"+h.cars[p]))
71 				er += TR("#{Vehicle}: ")+h.cars[p]+TR(" - #{DoesntExist}.\n");
72 
73 		if (!er.empty())
74 		{	Message::createMessageBox(
75 				"Message", TR("#{Replay} - #{RplLoad} - #{Error}"), "\n"+er,
76 				MessageBoxStyle::IconError | MessageBoxStyle::Ok);
77 			return;
78 		}
79 		//trackreverse 	num_laps
80 		//collis_veget, collis_cars, collis_roadw, dyn_objects;
81 		//int boost_type, flip_type;  float boost_power;
82 		//float pre_time;
83 
84 		//  set game config from replay
85 		pSet->game = pSet->gui;
86 		pSet->game.track = trk;  pSet->game.track_user = usr;
87 
88 		pSet->game.trees = h.trees;
89 		pSet->game.local_players = h.numPlayers;
90 		BackFromChs();
91 		LogO("RPL btn Load  players: "+toStr(h.numPlayers)+" netw: "+ toStr(h.networked));
92 
93 		for (p=0; p < h.numPlayers; ++p)
94 		{	pSet->game.car[p] = h.cars[p];
95 		}
96 		app->newGameRpl = true;
97 		btnNewGame(0);
98 		app->bRplPlay = 1;  app->iRplSkip = 0;
99 	}
100 }
101 
btnRplSave(WP)102 void CGui::btnRplSave(WP)  // Save
103 {
104 	String edit = edRplName->getCaption();
105 	String file = PATHMANAGER::Replays() + "/" + pSet->game.track + "_" + edit + ".rpl";
106 	///  save
107 	if (PATHMANAGER::FileExists(file))
108 	{
109 		Message::createMessageBox(
110 			"Message", TR("#{Replay} - #{RplSave}"), TR("#{AlreadyExists}."),
111 			MessageBoxStyle::IconWarning | MessageBoxStyle::Ok);
112 		return;
113 	}
114 	if (!app->replay.SaveFile(file.c_str()))
115 	{
116 		Message::createMessageBox(
117 			"Message", TR("#{Replay} - #{RplSave}"), TR("#{Error}."),
118 			MessageBoxStyle::IconWarning | MessageBoxStyle::Ok);
119 	}
120 	updReplaysList();
121 }
122 
123 //  list change
listRplChng(List * li,size_t pos)124 void CGui::listRplChng(List* li, size_t pos)
125 {
126 	String name = getRplName();  if (name.empty())  return;
127 	string file = GetRplListDir() + "/" + name + ".rpl";
128 	valRplName->setCaption(name);
129 	edRplName->setCaption(name);
130 
131 	//  load replay header, upd info text
132 	Replay2 rpl;  char stm[128];
133 	if (rpl.LoadFile(file,true))
134 	{
135 		const ReplayHeader2& rh = rpl.header;
136 		String ss = String(TR("#{Track}: ")) + gcom->GetSceneryColor(rh.track) +
137 			rh.track + (rh.track_user ? "  *"+TR("#{TweakUser}")+"*" : "");
138 		valRplName->setCaption(ss);
139 		char pp = rh.numPlayers, netw = rh.networked, n;
140 
141 		ss = String(TR("#{Vehicles}: "));
142 		for (n=0; n < pp; ++n)  ss += rh.cars[n] + "  ";
143 		ss += //(netw == 0 ? "" : "M") +  //TR("#{Multiplayer}")
144 			"\n#C0D8F0" + TR("#{RplTime}: ") + StrTime(rpl.GetTimeLength()) +
145 			"\n#90A0B0" + TR("#{Simulation}: ") + rh.sim_mode;
146 
147 		if (netw == 1)  // list nicks
148 		{	ss += String("\n#90C0E0");
149 			for (n=0; n < pp; ++n)
150 				ss += rh.nicks[n]+"  ";
151 		}
152 		valRplInfo->setCaption(ss);
153 
154 		//  file stats
155 		int size = fs::file_size(file);
156 		std::time_t ti = fs::last_write_time(file);
157 		if (!std::strftime(stm, 126, "%d.%b'%y  %a %H:%M", std::localtime(&ti)))  stm[0]=0;
158 
159 		ss = String(stm)+"\n#A0A0A0"+  // date
160 			String(TR("#{RplFileSize}: ")) + fToStr( float(size)/1000000.f, 2,5) + TR(" #{UnitMB}") + "\n#808080" +
161 			TR("#{RplVersion}: ") + toStr(rh.ver);
162 		if (valRplInfo2)  valRplInfo2->setCaption(ss);
163 	}
164 	//edRplDesc
165 }
166 
167 
168 //  replay settings
169 
slRplNumViewports(SL)170 void CGui::slRplNumViewports(SL)
171 {
172 	int v = 1.f + 3.f * val;	if (bGI)  pSet->rpl_numViews = v;
173 	if (valRplNumViewports)  valRplNumViewports->setCaption(toStr(v));
174 }
175 
176 
177 //  replays list filtering
178 
btnRplAll(WP)179 void CGui::btnRplAll(WP)
180 {
181 	rbRplCur->setStateSelected(false);  rbRplAll->setStateSelected(true);
182 	pSet->rpl_listview = 0;  updReplaysList();
183 }
184 
btnRplCur(WP)185 void CGui::btnRplCur(WP)
186 {
187 	rbRplCur->setStateSelected(true);  rbRplAll->setStateSelected(false);
188 	pSet->rpl_listview = 1;  updReplaysList();
189 }
190 
chkRplGhosts(Ck *)191 void CGui::chkRplGhosts(Ck*)
192 {
193 	updReplaysList();
194 }
195 
196 
197 //  replay controls
198 
btnRplToStart(WP)199 void CGui::btnRplToStart(WP)
200 {
201 	pGame->timer.RestartReplay(0);
202 }
203 
btnRplToEnd(WP)204 void CGui::btnRplToEnd(WP)
205 {
206 }
207 
btnRplBackDn(WP,int,int,MouseButton)208 void CGui::btnRplBackDn(WP, int, int, MouseButton){	bRplBack = true;  }
btnRplBackUp(WP,int,int,MouseButton)209 void CGui::btnRplBackUp(WP, int, int, MouseButton){	bRplBack = false;  }
btnRplFwdDn(WP,int,int,MouseButton)210 void CGui::btnRplFwdDn(WP, int, int, MouseButton){	bRplFwd = true;  }
btnRplFwdUp(WP,int,int,MouseButton)211 void CGui::btnRplFwdUp(WP, int, int, MouseButton){	bRplFwd = false;  }
212 
btnRplPlay(WP)213 void CGui::btnRplPlay(WP)  // play / pause
214 {
215 	app->bRplPause = !app->bRplPause;
216 	UpdRplPlayBtn();
217 }
218 
UpdRplPlayBtn()219 void CGui::UpdRplPlayBtn()
220 {
221 	String sign = app->bRplPause ? "|>" : "||";
222 	if (btRplPl)
223 		btRplPl->setCaption(sign);
224 }
225 
226 
227 //  List
updReplaysList()228 void CGui::updReplaysList()
229 {
230 	if (!rplList)  return;
231 	//Ogre::Timer ti;
232 
233 	rplList->removeAllItems();
234 
235 	strlist li;
236 	PATHMANAGER::DirList(GetRplListDir(), li, "rpl");
237 
238 	for (strlist::iterator i = li.begin(); i != li.end(); ++i)
239 	if (StringUtil::endsWith(*i, ".rpl"))
240 	{
241 		String s = *i;  s = StringUtil::replaceAll(s,".rpl","");
242 		String slow = s;  StringUtil::toLowerCase(slow);
243 		if (sRplFind == "" || strstr(slow.c_str(), sRplFind.c_str()) != 0)
244 		if (pSet->rpl_listview != 1 || StringUtil::startsWith(s,pSet->gui.track, false))
245 		{	String t = s;
246 			size_t p = t.find_first_of("_");
247 			if (p != string::npos)
248 				t = s.substr(0, p);
249 			rplList->addItem(gcom->GetSceneryColor(t) + s);
250 	}	}
251 	//LogO(String("::: Time ReplaysList: ") + fToStr(ti.getMilliseconds(),0,3) + " ms");
252 }
253 
254 
255 //  Delete
btnRplDelete(WP)256 void CGui::btnRplDelete(WP)
257 {
258 	String name = getRplName();  if (name.empty())  return;
259 
260 	Message* message = Message::createMessageBox(
261 		"Message", TR("#{Replay} - #{RplDelete} ?"), name,
262 		MessageBoxStyle::IconQuest | MessageBoxStyle::Yes | MessageBoxStyle::No);
263 	message->eventMessageBoxResult += newDelegate(this, &CGui::msgRplDelete);
264 }
msgRplDelete(Message * sender,MessageBoxStyle result)265 void CGui::msgRplDelete(Message* sender, MessageBoxStyle result)
266 {
267 	if (result != MessageBoxStyle::Yes)  return;
268 	String name = getRplName();  if (name.empty())  return;
269 	string file = GetRplListDir() +"/"+ name + ".rpl";
270 
271 	if (fs::exists(file))
272 		fs::remove(file);
273 	updReplaysList();
274 }
275 
276 //  Rename
btnRplRename(WP)277 void CGui::btnRplRename(WP)
278 {
279 	if (pSet->rpl_listghosts)  return;  // cant rename ghosts
280 	String name = getRplName();  if (name.empty())  return;
281 	string edit = edRplName->getCaption();
282 
283 	if (name == edit)  // same name
284 	{	Message::createMessageBox(
285 			"Message", TR("#{Replay} - #{RplRename}"), TR("#{AlreadyExists}."),
286 			MessageBoxStyle::IconInfo | MessageBoxStyle::Ok);
287 		return;  }
288 
289 	string file = PATHMANAGER::Replays() + "/" + name + ".rpl";
290 	string fileNew = PATHMANAGER::Replays() + "/" + edit + ".rpl";
291 
292 	if (fs::exists(fileNew))
293 	{	Message::createMessageBox(
294 			"Message", TR("#{Replay} - #{RplRename}"), TR("#{AlreadyExists}."),
295 			MessageBoxStyle::IconInfo | MessageBoxStyle::Ok);
296 		return;  }
297 
298 	if (fs::exists(file))
299 		fs::rename(file, fileNew);
300 	updReplaysList();
301 }
302 
303 
304 
305 //  Game replay Tools
306 //--------------------------------------------------------------------------------
fixOldTrkName(string & s)307 bool Replay::fixOldTrkName(string& s)
308 {
309 	if (s.length() <= 4)  return false;
310 
311 	if (s[0]=='0' && s[1]=='W')
312 		s = s.substr(1);
313 	if (s[0]>='A' && s[0]<='Z' && (s[2]=='-' || s[3]=='-'))
314 	{
315 		if (s[0]=='J')  s= "Jng" +s.substr(1);  else
316 		if (s[0]=='D')  s= "Des" +s.substr(1);  else
317 		if (s[0]=='S')  s= "Sav" +s.substr(1);  else
318 		if (s[0]=='W')  s= "Wnt" +s.substr(1);  else
319 		if (s[0]=='F')  s= "For" +s.substr(1);  else
320 		if (s[0]=='E')  s= "Fin" +s.substr(1);  else
321 		if (s[0]=='I')  s= "Isl" +s.substr(1);  else
322 		if (s[0]=='M')  s= "Mud" +s.substr(1);  else
323 		if (s[0]=='A')  s= "Aus" +s.substr(1);  else
324 		if (s[0]=='G')  s= "Grc" +s.substr(1);  else
325 		if (s[0]=='C')  s= "Can" +s.substr(1);  else
326 		if (s[0]=='T')  s= "Atm" +s.substr(1);  else
327 		if (s[0]=='O')  s= "Mos" +s.substr(1);  else
328 		if (s[0]=='V')  s= "Vlc" +s.substr(1);  else
329 		if (s[0]=='X')  s= "Uni" +s.substr(1);  else
330 		if (s[0]=='R')  s= "Mrs" +s.substr(1);  else
331 		if (s[0]=='Y')  s= "Cry" +s.substr(1);  else  return false;
332 		return true;
333 	}
334 	return false;
335 }
336 
337 //  Rename old track names
btnRenameOldTrk(WP)338 void CGui::btnRenameOldTrk(WP)
339 {
340 	LogO("==------  Renaming old replays and ghosts");
341 	std::vector<string> pp;
342 	pp.push_back(PATHMANAGER::Replays());
343 	pp.push_back(PATHMANAGER::Ghosts() +"/easy");
344 	pp.push_back(PATHMANAGER::Ghosts() +"/normal");
345 	pp.push_back(PATHMANAGER::Records()+"/easy");
346 	pp.push_back(PATHMANAGER::Records()+"/normal");
347 	strlist li;
348 	for (int ip=0; ip < pp.size(); ++ip)
349 	{
350 		li.clear();
351 		string p = pp[ip];
352 		PATHMANAGER::DirList(p, li);
353 		LogO("PATH: "+p);
354 		for (strlist::iterator i = li.begin(); i != li.end(); ++i)
355 		{
356 			String s = *i, sn;
357 			if (s.length() > 3)
358 			if (s[0]>='A' && s[0]<='Z' && (s[2]=='-' || s[3]=='-'))
359 			{	sn = s;
360 				if (Replay::fixOldTrkName(sn))
361 				if (!fs::exists(p+"/"+s))       LogO(s+" to "+sn+"  source doesnt exist!");
362 				/**/else if (fs::exists(p+"/"+sn))  LogO(s+" to "+sn+"  destination already exists");
363 				else
364 				{	LogO(s+" to "+sn);
365 					fs::rename(p+"/"+s, p+"/"+sn);
366 			}	}
367 	}	}
368 	LogO("==------  Renaming End");
369 }
370 
371 //  Convert to Replay2
btnConvertAllRpl(WP)372 void CGui::btnConvertAllRpl(WP)
373 {
374 	if (bConvertRpl)  return;
375 	bConvertRpl = true;
376 
377 	txtConvert->setVisible(true);
378 
379 	iConvCur = -1;  iConvAll = 1;
380 	mThrConvert = boost::thread(boost::bind(&CGui::ThreadConvert, boost::ref(*this)));
381 }
382 
ThreadConvert()383 void CGui::ThreadConvert()
384 {
385 	LogO("====----  Converting old replays and ghosts");
386 	Ogre::Timer ti;
387 
388 	std::vector<string> paths, files[3];
389 	//  paths
390 	paths.push_back(PATHMANAGER::Ghosts() +"/easy");
391 	paths.push_back(PATHMANAGER::Ghosts() +"/normal");
392 	paths.push_back(PATHMANAGER::Replays());
393 
394 	iConvPathCur = 0;  iConvPathAll = paths.size();
395 	iConvFiles = 0;  totalConv = 0;  totalConvNew = 0;  totalConvCur = 0;
396 
397 	//  List files  ------------
398 	strlist li;  int p;
399 	Replay2 rpl;
400 	for (p=0; p < iConvPathAll; ++p)
401 	{	iConvPathCur = p;
402 
403 		const string& path = paths[p];
404 		li.clear();
405 		PATHMANAGER::DirList(path, li, "rpl");
406 		iConvCur = 0;  iConvAll = li.size();
407 
408 		for (strlist::iterator i = li.begin(); i != li.end(); ++i)
409 		{
410 			if (app->mShutDown)  return;
411 			String file = *i, s = path +"/"+ file;
412 
413 			//Replay2 rpl;
414 			rpl.LoadFile(s, true);  // header
415 			if (rpl.header.ver <= 10)  // old, not converted, 10 was last 2.5
416 			{
417 				++iConvFiles;
418 				files[p].push_back(file);
419 				boost::uintmax_t size = fs::file_size(s);
420 				totalConv += size;
421 			}
422 			++iConvCur;
423 		}
424 		LogO("PATH: "+path+" total size:   "+fToStr( float(totalConv)/1000000.f, 2,5)+" MiB");
425 		LogO("PATH: "+path+" total after:  "+fToStr( float(totalConvNew)/1000000.f, 2,5)+" MiB");
426 	}
427 	LogO(String("::: Time Convert get list: ") + fToStr(ti.getMilliseconds()/1000.f,1,4) + " s");
428 	LogO("====----  Converting Start");
429 
430 	iConvCur = 0;  iConvAll = iConvFiles;
431 
432 
433 	LogO(String("FILES to convert: ") + toStr(iConvFiles));
434 
435 	//  Convert  ------------
436 	for (p=0; p < iConvPathAll; ++p)
437 	{	iConvPathCur = p;
438 
439 		const string& path = paths[p];
440 		iConvCur = 0;  iConvAll = files[p].size();
441 
442 		while (iConvCur < iConvAll && !app->mShutDown)
443 		{
444 			const string s = path +"/"+ files[p][iConvCur];
445 			//Replay2 rpl;
446 			rpl.LoadFile(s);  // converts old
447 			boost::uintmax_t size = fs::file_size(s);  // for progress
448 			totalConvCur += size;
449 			std::time_t tim = fs::last_write_time(s);
450 
451 			rpl.SaveFile(s);  // same name, no backup
452 			fs::last_write_time(s, tim);  // restore original date
453 			boost::uintmax_t sizeNew = fs::file_size(s);
454 			totalConvNew += sizeNew;
455 			++iConvCur;
456 		}
457 	}
458 
459 	//  Results  ------------
460 	bConvertRpl = false;
461 	LogO("====----  Converting Results");
462 	LogO("  Sizes");
463 	LogO("  old:   "+ fToStr( float(totalConv)/1000000.f, 2,5) +" MiB");
464 	LogO("  new:  "+ fToStr( float(totalConvNew)/1000000.f, 2,5) +" MiB");
465 	if (totalConvCur!=totalConv || totalConv==0){} else
466 	LogO("  ratio:  "+ fToStr(100.f* float(totalConvNew)/float(totalConv), 2,5) +" %");
467 
468 	LogO(String("::: Time Convert: ") + fToStr(ti.getMilliseconds()/1000.f,1,4) + " s");
469 	LogO("====----  Converting End");
470 }
471