70 |
61 |
71 return load_index - load_index_org; |
62 return load_index - load_index_org; |
72 } |
63 } |
73 |
64 |
74 |
65 |
75 static void LoadGrfIndexed(const char* filename, const SpriteID* index_tbl, int file_index) |
66 void LoadSpritesIndexed(int file_index, uint *sprite_id, const SpriteID *index_tbl) |
76 { |
67 { |
77 uint start; |
68 uint start; |
78 uint sprite_id = 0; |
|
79 |
|
80 FioOpenFile(file_index, filename); |
|
81 |
|
82 DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename); |
|
83 |
|
84 while ((start = *index_tbl++) != END) { |
69 while ((start = *index_tbl++) != END) { |
85 uint end = *index_tbl++; |
70 uint end = *index_tbl++; |
86 |
71 |
87 if (start == SKIP) { // skip sprites (amount in second var) |
72 if (start == SKIP) { // skip sprites (amount in second var) |
88 SkipSprites(end); |
73 SkipSprites(end); |
89 sprite_id += end; |
74 (*sprite_id) += end; |
90 } else { // load sprites and use indexes from start to end |
75 } else { // load sprites and use indexes from start to end |
91 do { |
76 do { |
92 #ifdef NDEBUG |
77 #ifdef NDEBUG |
93 LoadNextSprite(start, file_index, sprite_id); |
78 LoadNextSprite(start, file_index, *sprite_id); |
94 #else |
79 #else |
95 bool b = LoadNextSprite(start, file_index, sprite_id); |
80 bool b = LoadNextSprite(start, file_index, *sprite_id); |
96 assert(b); |
81 assert(b); |
97 #endif |
82 #endif |
98 sprite_id++; |
83 (*sprite_id)++; |
99 } while (++start <= end); |
84 } while (++start <= end); |
100 } |
85 } |
101 } |
86 } |
102 } |
87 } |
103 |
88 |
104 |
89 static void LoadGrfIndexed(const char* filename, const SpriteID* index_tbl, int file_index) |
105 /* Check that the supplied MD5 hash matches that stored for the supplied filename */ |
90 { |
106 static bool CheckMD5Digest(const MD5File file, md5_byte_t *digest, bool warn) |
91 uint sprite_id = 0; |
107 { |
92 |
108 if (memcmp(file.hash, digest, sizeof(file.hash)) == 0) return true; |
93 FioOpenFile(file_index, filename); |
109 if (warn) fprintf(stderr, "MD5 of %s is ****INCORRECT**** - File Corrupt.\n", file.filename); |
94 |
110 return false; |
95 DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename); |
111 } |
96 |
112 |
97 LoadSpritesIndexed(file_index, &sprite_id, index_tbl); |
113 /* Calculate and check the MD5 hash of the supplied filename. |
98 } |
114 * returns true if the checksum is correct */ |
99 |
115 static bool FileMD5(const MD5File file, bool warn) |
100 |
|
101 /** |
|
102 * Calculate and check the MD5 hash of the supplied filename. |
|
103 * @param file filename and expected MD5 hash for the given filename. |
|
104 * @return true if the checksum is correct. |
|
105 */ |
|
106 static bool FileMD5(const MD5File file) |
116 { |
107 { |
117 size_t size; |
108 size_t size; |
118 FILE *f = FioFOpenFile(file.filename, "rb", DATA_DIR, &size); |
109 FILE *f = FioFOpenFile(file.filename, "rb", DATA_DIR, &size); |
119 |
110 |
120 if (f != NULL) { |
111 if (f != NULL) { |
127 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) { |
118 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) { |
128 size -= len; |
119 size -= len; |
129 md5_append(&filemd5state, buffer, len); |
120 md5_append(&filemd5state, buffer, len); |
130 } |
121 } |
131 |
122 |
132 if (ferror(f) && warn) ShowInfoF("Error Reading from %s \n", file.filename); |
|
133 FioFCloseFile(f); |
123 FioFCloseFile(f); |
134 |
124 |
135 md5_finish(&filemd5state, digest); |
125 md5_finish(&filemd5state, digest); |
136 return CheckMD5Digest(file, digest, warn); |
126 return memcmp(file.hash, digest, sizeof(file.hash)) == 0; |
137 } else { // file not found |
127 } else { // file not found |
138 return false; |
128 return false; |
139 } |
129 } |
140 } |
130 } |
141 |
131 |
142 /* Checks, if either the Windows files exist (TRG1R.GRF) or the DOS files (TRG1.GRF) |
132 /** |
143 * by comparing the MD5 checksums of the files. _use_dos_palette is set accordingly. |
133 * Determine the palette that has to be used. |
144 * If neither are found, Windows palette is assumed. |
134 * - forced DOS palette via command line -> leave it that way |
145 * |
135 * - all Windows files present -> Windows palette |
146 * (Note: Also checks sample.cat for corruption) */ |
136 * - all DOS files present -> DOS palette |
147 void CheckExternalFiles() |
137 * - no Windows files present and any DOS file present -> DOS palette |
148 { |
138 * - otherwise -> Windows palette |
149 uint i; |
139 */ |
150 /* count of files from this version */ |
140 static void DeterminePalette() |
|
141 { |
|
142 if (_use_dos_palette) return; |
|
143 |
|
144 /* Count of files from the different versions. */ |
151 uint dos = 0; |
145 uint dos = 0; |
152 uint win = 0; |
146 uint win = 0; |
153 |
147 |
154 for (i = 0; i < 2; i++) if (FileMD5(files_dos.basic[i], true)) dos++; |
148 for (uint i = 0; i < lengthof(files_dos.basic); i++) if (FioCheckFileExists(files_dos.basic[i].filename)) dos++; |
155 for (i = 0; i < 3; i++) if (FileMD5(files_dos.landscape[i], true)) dos++; |
149 for (uint i = 0; i < lengthof(files_dos.landscape); i++) if (FioCheckFileExists(files_dos.landscape[i].filename)) dos++; |
156 |
150 |
157 for (i = 0; i < 2; i++) if (FileMD5(files_win.basic[i], true)) win++; |
151 for (uint i = 0; i < lengthof(files_win.basic); i++) if (FioCheckFileExists(files_win.basic[i].filename)) win++; |
158 for (i = 0; i < 3; i++) if (FileMD5(files_win.landscape[i], true)) win++; |
152 for (uint i = 0; i < lengthof(files_win.landscape); i++) if (FioCheckFileExists(files_win.landscape[i].filename)) win++; |
159 |
153 |
160 if (!FileMD5(sample_cat_win, false) && !FileMD5(sample_cat_dos, false)) |
154 if (win == 5) { |
161 ShowInfo("Your 'sample.cat' file is corrupted or missing!"); |
|
162 |
|
163 for (i = 0; i < lengthof(files_openttd); i++) { |
|
164 if (!FileMD5(files_openttd[i], false)) { |
|
165 ShowInfoF("Your '%s' file is corrupted or missing!", files_openttd[i].filename); |
|
166 } |
|
167 } |
|
168 |
|
169 /* |
|
170 * forced DOS palette via command line -> leave it that way |
|
171 * all Windows files present -> Windows palette |
|
172 * all DOS files present -> DOS palette |
|
173 * no Windows files present and any DOS file present -> DOS palette |
|
174 * otherwise -> Windows palette |
|
175 */ |
|
176 if (_use_dos_palette) { |
|
177 return; |
|
178 } else if (win == 5) { |
|
179 _use_dos_palette = false; |
155 _use_dos_palette = false; |
180 } else if (dos == 5 || (win == 0 && dos > 0)) { |
156 } else if (dos == 5 || (win == 0 && dos > 0)) { |
181 _use_dos_palette = true; |
157 _use_dos_palette = true; |
182 } else { |
158 } else { |
183 _use_dos_palette = false; |
159 _use_dos_palette = false; |
184 } |
160 } |
|
161 } |
|
162 |
|
163 /** |
|
164 * Checks whether the MD5 checksums of the files are correct. |
|
165 * |
|
166 * @note Also checks sample.cat and other required non-NewGRF GRFs for corruption. |
|
167 */ |
|
168 void CheckExternalFiles() |
|
169 { |
|
170 DeterminePalette(); |
|
171 |
|
172 static const size_t ERROR_MESSAGE_LENGTH = 128; |
|
173 const FileList *files = _use_dos_palette ? &files_dos : &files_win; |
|
174 char error_msg[ERROR_MESSAGE_LENGTH * (lengthof(files->basic) + lengthof(files->landscape) + 3)]; |
|
175 error_msg[0] = '\0'; |
|
176 char *add_pos = error_msg; |
|
177 |
|
178 for (uint i = 0; i < lengthof(files->basic); i++) { |
|
179 if (!FileMD5(files->basic[i])) { |
|
180 add_pos += snprintf(add_pos, ERROR_MESSAGE_LENGTH, "Your '%s' file is corrupted or missing! You can find '%s' on your Transport Tycoon Deluxe CD-ROM.\n", files->basic[i].filename, files->basic[i].filename); |
|
181 } |
|
182 } |
|
183 |
|
184 for (uint i = 0; i < lengthof(files->landscape); i++) { |
|
185 if (!FileMD5(files->landscape[i])) { |
|
186 add_pos += snprintf(add_pos, ERROR_MESSAGE_LENGTH, "Your '%s' file is corrupted or missing! You can find '%s' on your Transport Tycoon Deluxe CD-ROM.\n", files->landscape[i].filename, files->landscape[i].filename); |
|
187 } |
|
188 } |
|
189 |
|
190 if (!FileMD5(files_win.sound) && !FileMD5(files_dos.sound)) { |
|
191 add_pos += snprintf(add_pos, ERROR_MESSAGE_LENGTH, "Your 'sample.cat' file is corrupted or missing! You can find 'sample.cat' on your Transport Tycoon Deluxe CD-ROM.\n"); |
|
192 } |
|
193 |
|
194 if (!FileMD5(files->chars)) { |
|
195 add_pos += snprintf(add_pos, ERROR_MESSAGE_LENGTH, "Your '%s' file is corrupted or missing! The file was part of your installation.\n", files->chars.filename); |
|
196 } |
|
197 |
|
198 if (!FileMD5(files->openttd)) { |
|
199 add_pos += snprintf(add_pos, ERROR_MESSAGE_LENGTH, "Your '%s' file is corrupted or missing! The file was part of your installation.\n", files->openttd.filename); |
|
200 } |
|
201 |
|
202 if (add_pos != error_msg) ShowInfoF(error_msg); |
185 } |
203 } |
186 |
204 |
187 |
205 |
188 static const SpriteID trg1idx[] = { |
206 static const SpriteID trg1idx[] = { |
189 0, 1, ///< Mouse cursor, ZZZ |
207 0, 1, ///< Mouse cursor, ZZZ |
263 /* Graphics */ |
281 /* Graphics */ |
264 674, 4792, |
282 674, 4792, |
265 END |
283 END |
266 }; |
284 }; |
267 |
285 |
268 /* NOTE: When adding a normal sprite, increase OPENTTD_SPRITES_COUNT with the |
286 /** Replace some letter sprites with some other letters */ |
269 * amount of sprites and add them to the end of the list, with the index of |
287 static const SpriteID _chars_grf_indexes[] = { |
270 * the old sprite-count offset from SPR_OPENTTD_BASE. With this there is no |
288 134, 134, ///< euro symbol medium size |
271 * correspondence of any kind with the ID's in the grf file, but results in |
289 582, 582, ///< euro symbol large size |
272 * a maximum use of sprite slots. */ |
290 358, 358, ///< euro symbol tiny |
273 static const SpriteID _openttd_grf_indexes[] = { |
|
274 SPR_IMG_AUTORAIL, SPR_CURSOR_WAYPOINT, // icons etc |
|
275 134, 134, ///< euro symbol medium size |
|
276 582, 582, ///< euro symbol large size |
|
277 358, 358, ///< euro symbol tiny |
|
278 SPR_CURSOR_CANAL, SPR_IMG_FASTFORWARD, // more icons |
|
279 648, 648, ///< nordic char: æ |
291 648, 648, ///< nordic char: æ |
280 616, 616, ///< nordic char: Æ |
292 616, 616, ///< nordic char: Æ |
281 666, 666, ///< nordic char: ø |
293 666, 666, ///< nordic char: ø |
282 634, 634, ///< nordic char: Ø |
294 634, 634, ///< nordic char: Ø |
283 SPR_PIN_UP, SPR_CURSOR_CLONE_TRAIN, // more icons |
|
284 382, 383, ///< Œ œ tiny |
295 382, 383, ///< Œ œ tiny |
285 158, 159, ///< Œ œ medium |
296 158, 159, ///< Œ œ medium |
286 606, 607, ///< Œ œ large |
297 606, 607, ///< Œ œ large |
287 360, 360, ///< Š tiny |
298 360, 360, ///< Š tiny |
288 362, 362, ///< š tiny |
299 362, 362, ///< š tiny |
304 156, 156, ///< º medium |
315 156, 156, ///< º medium |
305 604, 604, ///< º large |
316 604, 604, ///< º large |
306 317, 320, ///< { | } ~ tiny |
317 317, 320, ///< { | } ~ tiny |
307 93, 96, ///< { | } ~ medium |
318 93, 96, ///< { | } ~ medium |
308 541, 544, ///< { | } ~ large |
319 541, 544, ///< { | } ~ large |
309 SPR_HOUSE_ICON, SPR_HOUSE_ICON, |
|
310 585, 585, ///< § large |
320 585, 585, ///< § large |
311 587, 587, ///< © large |
321 587, 587, ///< © large |
312 592, 592, ///< ® large |
322 592, 592, ///< ® large |
313 594, 597, ///< ° ± ² ³ large |
323 594, 597, ///< ° ± ² ³ large |
314 633, 633, ///< × large |
324 633, 633, ///< × large |
315 665, 665, ///< ÷ large |
325 665, 665, ///< ÷ large |
316 SPR_SELL_TRAIN, SPR_SHARED_ORDERS_ICON, |
|
317 377, 377, ///< · small |
326 377, 377, ///< · small |
318 153, 153, ///< · medium |
327 153, 153, ///< · medium |
319 601, 601, ///< · large |
328 601, 601, ///< · large |
320 SPR_WARNING_SIGN, SPR_WARNING_SIGN, |
|
321 END |
329 END |
322 }; |
330 }; |
323 |
331 |
324 |
332 |
325 static void LoadSpriteTables() |
333 static void LoadSpriteTables() |
326 { |
334 { |
327 const FileList* files = _use_dos_palette ? &files_dos : &files_win; |
335 const FileList *files = _use_dos_palette ? &files_dos : &files_win; |
328 uint load_index; |
336 uint i = FIRST_GRF_SLOT; |
329 uint i; |
337 |
330 |
338 LoadGrfIndexed(files->basic[0].filename, trg1idx, i++); |
331 LoadGrfIndexed(files->basic[0].filename, trg1idx, 0); |
|
332 DupSprite( 2, 130); // non-breaking space medium |
339 DupSprite( 2, 130); // non-breaking space medium |
333 DupSprite(226, 354); // non-breaking space tiny |
340 DupSprite(226, 354); // non-breaking space tiny |
334 DupSprite(450, 578); // non-breaking space large |
341 DupSprite(450, 578); // non-breaking space large |
335 load_index = 4793; |
342 |
336 |
343 /* |
337 for (i = 1; files->basic[i].filename != NULL; i++) { |
344 * The second basic file always starts at the given location and does |
338 load_index += LoadGrfFile(files->basic[i].filename, load_index, i); |
345 * contain a different amount of sprites depending on the "type"; DOS |
339 } |
346 * has a few sprites less. However, we do not care about those missing |
340 |
347 * sprites as they are not shown anyway (logos in intro game). |
341 /* Load additional sprites for climates other than temperate */ |
348 */ |
|
349 LoadGrfFile(files->basic[1].filename, 4793, i++); |
|
350 |
|
351 /* |
|
352 * Load additional sprites for climates other than temperate. |
|
353 * This overwrites some of the temperate sprites, such as foundations |
|
354 * and the ground sprites. |
|
355 */ |
342 if (_opt.landscape != LT_TEMPERATE) { |
356 if (_opt.landscape != LT_TEMPERATE) { |
343 LoadGrfIndexed( |
357 LoadGrfIndexed( |
344 files->landscape[_opt.landscape - 1].filename, |
358 files->landscape[_opt.landscape - 1].filename, |
345 _landscape_spriteindexes[_opt.landscape - 1], |
359 _landscape_spriteindexes[_opt.landscape - 1], |
346 i++ |
360 i++ |
347 ); |
361 ); |
348 } |
362 } |
349 |
363 |
350 assert(load_index == SPR_SIGNALS_BASE); |
364 LoadGrfIndexed(files->chars.filename, _chars_grf_indexes, i++); |
351 load_index += LoadGrfFile("nsignalsw.grf", load_index, i++); |
|
352 |
|
353 assert(load_index == SPR_CANALS_BASE); |
|
354 load_index += LoadGrfFile("canalsw.grf", load_index, i++); |
|
355 |
|
356 assert(load_index == SPR_SLOPES_BASE); |
|
357 LoadGrfIndexed("trkfoundw.grf", _slopes_spriteindexes[_opt.landscape], i++); |
|
358 |
|
359 load_index = SPR_AUTORAIL_BASE; |
|
360 load_index += LoadGrfFile("autorail.grf", load_index, i++); |
|
361 |
|
362 assert(load_index == SPR_ELRAIL_BASE); |
|
363 load_index += LoadGrfFile("elrailsw.grf", load_index, i++); |
|
364 |
|
365 assert(load_index == SPR_2CCMAP_BASE); |
|
366 load_index += LoadGrfFile("2ccmap.grf", load_index, i++); |
|
367 |
|
368 assert(load_index == SPR_OPENTTD_BASE); |
|
369 LoadGrfIndexed("openttd.grf", _openttd_grf_indexes, i++); |
|
370 load_index = SPR_OPENTTD_BASE + OPENTTD_SPRITES_COUNT; |
|
371 |
|
372 assert(load_index == SPR_AIRPORTX_BASE); |
|
373 load_index += LoadGrfFile("airports.grf", load_index, i++); |
|
374 |
|
375 assert(load_index == SPR_ROADSTOP_BASE); |
|
376 load_index += LoadGrfFile("roadstops.grf", load_index, i++); |
|
377 |
|
378 assert(load_index == SPR_GROUP_BASE); |
|
379 load_index += LoadGrfFile("group.grf", load_index, i++); |
|
380 |
|
381 assert(load_index == SPR_TRAMWAY_BASE); |
|
382 load_index += LoadGrfFile("tramtrkw.grf", load_index, i++); |
|
383 |
|
384 assert(load_index == SPR_ONEWAY_BASE); |
|
385 load_index += LoadGrfFile("oneway.grf", load_index, i++); |
|
386 |
|
387 load_index++; // SPR_EMPTY_BOUNDING_BOX |
|
388 |
|
389 assert(load_index == SPR_FLAGS_BASE); |
|
390 load_index += LoadGrfFile("flags.grf", load_index, i++); |
|
391 |
365 |
392 /* Initialize the unicode to sprite mapping table */ |
366 /* Initialize the unicode to sprite mapping table */ |
393 InitializeUnicodeGlyphMap(); |
367 InitializeUnicodeGlyphMap(); |
394 |
368 |
395 LoadNewGRF(load_index, i); |
369 /* |
|
370 * Load the base NewGRF with OTTD required graphics as first NewGRF. |
|
371 * However, we do not want it to show up in the list of used NewGRFs, |
|
372 * so we have to manually add it, and then remove it later. |
|
373 */ |
|
374 GRFConfig *top = _grfconfig; |
|
375 GRFConfig *master = CallocT<GRFConfig>(1); |
|
376 master->filename = strdup(files->openttd.filename); |
|
377 FillGRFDetails(master, false); |
|
378 ClrBit(master->flags, GCF_INIT_ONLY); |
|
379 master->next = top; |
|
380 _grfconfig = master; |
|
381 |
|
382 LoadNewGRF(SPR_NEWGRFS_BASE, i); |
|
383 |
|
384 /* Free and remove the top element. */ |
|
385 ClearGRFConfig(&master); |
|
386 _grfconfig = top; |
396 } |
387 } |
397 |
388 |
398 |
389 |
399 void GfxLoadSprites() |
390 void GfxLoadSprites() |
400 { |
391 { |