1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
8 /** @file highscore.cpp Definition of functions used for highscore handling */
9 
10 #include "stdafx.h"
11 #include "highscore.h"
12 #include "company_base.h"
13 #include "company_func.h"
14 #include "cheat_func.h"
15 #include "string_func.h"
16 #include "strings_func.h"
17 #include "table/strings.h"
18 #include "debug.h"
19 
20 #include "safeguards.h"
21 
22 HighScore _highscore_table[SP_HIGHSCORE_END][5]; ///< various difficulty-settings; top 5
23 std::string _highscore_file; ///< The file to store the highscore data in.
24 
25 static const StringID _endgame_perf_titles[] = {
26 	STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN,
27 	STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN,
28 	STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN,
29 	STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN,
30 	STR_HIGHSCORE_PERFORMANCE_TITLE_BUSINESSMAN,
31 	STR_HIGHSCORE_PERFORMANCE_TITLE_ENTREPRENEUR,
32 	STR_HIGHSCORE_PERFORMANCE_TITLE_ENTREPRENEUR,
33 	STR_HIGHSCORE_PERFORMANCE_TITLE_INDUSTRIALIST,
34 	STR_HIGHSCORE_PERFORMANCE_TITLE_INDUSTRIALIST,
35 	STR_HIGHSCORE_PERFORMANCE_TITLE_CAPITALIST,
36 	STR_HIGHSCORE_PERFORMANCE_TITLE_CAPITALIST,
37 	STR_HIGHSCORE_PERFORMANCE_TITLE_MAGNATE,
38 	STR_HIGHSCORE_PERFORMANCE_TITLE_MAGNATE,
39 	STR_HIGHSCORE_PERFORMANCE_TITLE_MOGUL,
40 	STR_HIGHSCORE_PERFORMANCE_TITLE_MOGUL,
41 	STR_HIGHSCORE_PERFORMANCE_TITLE_TYCOON_OF_THE_CENTURY
42 };
43 
EndGameGetPerformanceTitleFromValue(uint value)44 StringID EndGameGetPerformanceTitleFromValue(uint value)
45 {
46 	value = std::min<uint>(value / 64, lengthof(_endgame_perf_titles) - 1);
47 
48 	return _endgame_perf_titles[value];
49 }
50 
51 /** Save the highscore for the company */
SaveHighScoreValue(const Company * c)52 int8 SaveHighScoreValue(const Company *c)
53 {
54 	HighScore *hs = _highscore_table[SP_CUSTOM];
55 	uint i;
56 	uint16 score = c->old_economy[0].performance_history;
57 
58 	/* Exclude cheaters from the honour of being in the highscore table */
59 	if (CheatHasBeenUsed()) return -1;
60 
61 	for (i = 0; i < lengthof(_highscore_table[0]); i++) {
62 		/* You are in the TOP5. Move all values one down and save us there */
63 		if (hs[i].score <= score) {
64 			/* move all elements one down starting from the replaced one */
65 			memmove(&hs[i + 1], &hs[i], sizeof(HighScore) * (lengthof(_highscore_table[0]) - i - 1));
66 			SetDParam(0, c->index);
67 			SetDParam(1, c->index);
68 			GetString(hs[i].company, STR_HIGHSCORE_NAME, lastof(hs[i].company)); // get manager/company name string
69 			hs[i].score = score;
70 			hs[i].title = EndGameGetPerformanceTitleFromValue(score);
71 			return i;
72 		}
73 	}
74 
75 	return -1; // too bad; we did not make it into the top5
76 }
77 
78 /** Sort all companies given their performance */
HighScoreSorter(const Company * const & a,const Company * const & b)79 static bool HighScoreSorter(const Company * const &a, const Company * const &b)
80 {
81 	return b->old_economy[0].performance_history < a->old_economy[0].performance_history;
82 }
83 
84 /**
85  * Save the highscores in a network game when it has ended
86  * @return Position of the local company in the highscore list.
87  */
SaveHighScoreValueNetwork()88 int8 SaveHighScoreValueNetwork()
89 {
90 	const Company *cl[MAX_COMPANIES];
91 	uint count = 0;
92 	int8 company = -1;
93 
94 	/* Sort all active companies with the highest score first */
95 	for (const Company *c : Company::Iterate()) cl[count++] = c;
96 
97 	std::sort(std::begin(cl), std::begin(cl) + count, HighScoreSorter);
98 
99 	{
100 		uint i;
101 
102 		memset(_highscore_table[SP_MULTIPLAYER], 0, sizeof(_highscore_table[SP_MULTIPLAYER]));
103 
104 		/* Copy over Top5 companies */
105 		for (i = 0; i < lengthof(_highscore_table[SP_MULTIPLAYER]) && i < count; i++) {
106 			HighScore *hs = &_highscore_table[SP_MULTIPLAYER][i];
107 
108 			SetDParam(0, cl[i]->index);
109 			SetDParam(1, cl[i]->index);
110 			GetString(hs->company, STR_HIGHSCORE_NAME, lastof(hs->company)); // get manager/company name string
111 			hs->score = cl[i]->old_economy[0].performance_history;
112 			hs->title = EndGameGetPerformanceTitleFromValue(hs->score);
113 
114 			/* get the ranking of the local company */
115 			if (cl[i]->index == _local_company) company = i;
116 		}
117 	}
118 
119 	/* Add top5 companies to highscore table */
120 	return company;
121 }
122 
123 /** Save HighScore table to file */
SaveToHighScore()124 void SaveToHighScore()
125 {
126 	FILE *fp = fopen(_highscore_file.c_str(), "wb");
127 
128 	if (fp != nullptr) {
129 		uint i;
130 		HighScore *hs;
131 
132 		for (i = 0; i < SP_SAVED_HIGHSCORE_END; i++) {
133 			for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) {
134 				/* First character is a command character, so strlen will fail on that */
135 				byte length = std::min(sizeof(hs->company), StrEmpty(hs->company) ? 0 : strlen(&hs->company[1]) + 1);
136 
137 				if (fwrite(&length, sizeof(length), 1, fp)       != 1 || // write away string length
138 						fwrite(hs->company, length, 1, fp)           >  1 || // Yes... could be 0 bytes too
139 						fwrite(&hs->score, sizeof(hs->score), 1, fp) != 1 ||
140 						fwrite("  ", 2, 1, fp)                       != 1) { // XXX - placeholder for hs->title, not saved anymore; compatibility
141 					Debug(misc, 1, "Could not save highscore.");
142 					i = SP_SAVED_HIGHSCORE_END;
143 					break;
144 				}
145 			}
146 		}
147 		fclose(fp);
148 	}
149 }
150 
151 /** Initialize the highscore table to 0 and if any file exists, load in values */
LoadFromHighScore()152 void LoadFromHighScore()
153 {
154 	FILE *fp = fopen(_highscore_file.c_str(), "rb");
155 
156 	memset(_highscore_table, 0, sizeof(_highscore_table));
157 
158 	if (fp != nullptr) {
159 		uint i;
160 		HighScore *hs;
161 
162 		for (i = 0; i < SP_SAVED_HIGHSCORE_END; i++) {
163 			for (hs = _highscore_table[i]; hs != endof(_highscore_table[i]); hs++) {
164 				byte length;
165 				if (fread(&length, sizeof(length), 1, fp)                              !=  1 ||
166 						fread(hs->company, std::min<int>(lengthof(hs->company), length), 1, fp) >   1 || // Yes... could be 0 bytes too
167 						fread(&hs->score, sizeof(hs->score), 1, fp)                        !=  1 ||
168 						fseek(fp, 2, SEEK_CUR)                                             == -1) { // XXX - placeholder for hs->title, not saved anymore; compatibility
169 					Debug(misc, 1, "Highscore corrupted");
170 					i = SP_SAVED_HIGHSCORE_END;
171 					break;
172 				}
173 				StrMakeValidInPlace(hs->company, lastof(hs->company), SVS_NONE);
174 				hs->title = EndGameGetPerformanceTitleFromValue(hs->score);
175 			}
176 		}
177 		fclose(fp);
178 	}
179 }
180