00001
00002
00005 #include "stdafx.h"
00006 #include "debug.h"
00007 #include "spritecache.h"
00008 #include "fileio_func.h"
00009 #include "fios.h"
00010 #include "newgrf.h"
00011 #include "md5.h"
00012 #include "fontcache.h"
00013 #include "gfx_func.h"
00014 #include "settings_type.h"
00015 #include "string_func.h"
00016 #include "ini_type.h"
00017
00018 #include "table/sprites.h"
00019 #include "table/palette_convert.h"
00020
00022 PaletteType _use_palette = PAL_AUTODETECT;
00024 bool _palette_remap_grf[MAX_FILE_SLOTS];
00026 const byte *_palette_remap = NULL;
00028 const byte *_palette_reverse_remap = NULL;
00029
00030 char *_ini_graphics_set;
00031
00033 struct MD5File {
00034 const char *filename;
00035 uint8 hash[16];
00036 const char *missing_warning;
00037 };
00038
00040 enum GraphicsFileType {
00041 GFT_BASE,
00042 GFT_LOGOS,
00043 GFT_ARCTIC,
00044 GFT_TROPICAL,
00045 GFT_TOYLAND,
00046 GFT_EXTRA,
00047 MAX_GFT
00048 };
00049
00051 struct GraphicsSet {
00052 const char *name;
00053 const char *description;
00054 uint32 shortname;
00055 uint32 version;
00056 PaletteType palette;
00057
00058 MD5File files[MAX_GFT];
00059 uint found_grfs;
00060
00061 GraphicsSet *next;
00062
00064 ~GraphicsSet()
00065 {
00066 free((void*)this->name);
00067 free((void*)this->description);
00068 for (uint i = 0; i < MAX_GFT; i++) {
00069 free((void*)this->files[i].filename);
00070 free((void*)this->files[i].missing_warning);
00071 }
00072
00073 delete this->next;
00074 }
00075 };
00076
00078 static GraphicsSet *_available_graphics_sets = NULL;
00080 static const GraphicsSet *_used_graphics_set = NULL;
00081
00082 #include "table/files.h"
00083 #include "table/landscape_sprite.h"
00084
00085 static const SpriteID * const _landscape_spriteindexes[] = {
00086 _landscape_spriteindexes_1,
00087 _landscape_spriteindexes_2,
00088 _landscape_spriteindexes_3,
00089 };
00090
00091 static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
00092 {
00093 uint load_index_org = load_index;
00094 uint sprite_id = 0;
00095
00096 FioOpenFile(file_index, filename);
00097
00098 DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
00099
00100 while (LoadNextSprite(load_index, file_index, sprite_id)) {
00101 load_index++;
00102 sprite_id++;
00103 if (load_index >= MAX_SPRITES) {
00104 usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
00105 }
00106 }
00107 DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
00108
00109 return load_index - load_index_org;
00110 }
00111
00112
00113 void LoadSpritesIndexed(int file_index, uint *sprite_id, const SpriteID *index_tbl)
00114 {
00115 uint start;
00116 while ((start = *index_tbl++) != END) {
00117 uint end = *index_tbl++;
00118
00119 do {
00120 bool b = LoadNextSprite(start, file_index, *sprite_id);
00121 assert(b);
00122 (*sprite_id)++;
00123 } while (++start <= end);
00124 }
00125 }
00126
00127 static void LoadGrfIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
00128 {
00129 uint sprite_id = 0;
00130
00131 FioOpenFile(file_index, filename);
00132
00133 DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
00134
00135 LoadSpritesIndexed(file_index, &sprite_id, index_tbl);
00136 }
00137
00138
00144 static bool FileMD5(const MD5File file)
00145 {
00146 size_t size;
00147 FILE *f = FioFOpenFile(file.filename, "rb", DATA_DIR, &size);
00148
00149 if (f != NULL) {
00150 Md5 checksum;
00151 uint8 buffer[1024];
00152 uint8 digest[16];
00153 size_t len;
00154
00155 while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
00156 size -= len;
00157 checksum.Append(buffer, len);
00158 }
00159
00160 FioFCloseFile(f);
00161
00162 checksum.Finish(digest);
00163 return memcmp(file.hash, digest, sizeof(file.hash)) == 0;
00164 } else {
00165 return false;
00166 }
00167 }
00168
00173 static bool DetermineGraphicsPack()
00174 {
00175 if (_used_graphics_set != NULL) return true;
00176
00177 const GraphicsSet *best = _available_graphics_sets;
00178 for (const GraphicsSet *c = _available_graphics_sets; c != NULL; c = c->next) {
00179 if (best->found_grfs < c->found_grfs ||
00180 (best->found_grfs == c->found_grfs && (
00181 (best->shortname == c->shortname && best->version < c->version) ||
00182 (best->palette != _use_palette && c->palette == _use_palette)))) {
00183 best = c;
00184 }
00185 }
00186
00187 _used_graphics_set = best;
00188 return _used_graphics_set != NULL;
00189 }
00190
00191 extern void UpdateNewGRFConfigPalette();
00192
00198 static void DeterminePalette()
00199 {
00200 assert(_used_graphics_set != NULL);
00201 if (_use_palette >= MAX_PAL) _use_palette = _used_graphics_set->palette;
00202
00203 switch (_use_palette) {
00204 case PAL_DOS:
00205 _palette_remap = _palmap_w2d;
00206 _palette_reverse_remap = _palmap_d2w;
00207 break;
00208
00209 case PAL_WINDOWS:
00210 _palette_remap = _palmap_d2w;
00211 _palette_reverse_remap = _palmap_w2d;
00212 break;
00213
00214 default:
00215 NOT_REACHED();
00216 }
00217
00218 UpdateNewGRFConfigPalette();
00219 }
00220
00226 void CheckExternalFiles()
00227 {
00228 DeterminePalette();
00229
00230 DEBUG(grf, 1, "Using the %s base graphics set with the %s palette", _used_graphics_set->name, _use_palette == PAL_DOS ? "DOS" : "Windows");
00231
00232 static const size_t ERROR_MESSAGE_LENGTH = 256;
00233 static const size_t MISSING_FILE_MESSAGE_LENGTH = 128;
00234
00235
00236
00237
00238 char error_msg[MISSING_FILE_MESSAGE_LENGTH * (MAX_GFT + 1) + 2 * ERROR_MESSAGE_LENGTH];
00239 error_msg[0] = '\0';
00240 char *add_pos = error_msg;
00241 const char *last = lastof(error_msg);
00242
00243 if (_used_graphics_set->found_grfs != MAX_GFT) {
00244
00245 add_pos += seprintf(add_pos, last, "Trying to load graphics set '%s', but it is incomplete. The game will probably not run correctly until you properly install this set or select another one.\n\nThe following files are corrupted or missing:\n", _used_graphics_set->name);
00246 for (uint i = 0; i < lengthof(_used_graphics_set->files); i++) {
00247 if (!FileMD5(_used_graphics_set->files[i])) {
00248 add_pos += seprintf(add_pos, last, "\t%s (%s)\n", _used_graphics_set->files[i].filename, _used_graphics_set->files[i].missing_warning);
00249 }
00250 }
00251 add_pos += seprintf(add_pos, last, "\n");
00252 }
00253
00254 bool sound = false;
00255 for (uint i = 0; !sound && i < lengthof(_sound_sets); i++) {
00256 sound = FileMD5(_sound_sets[i]);
00257 }
00258
00259 if (!sound) {
00260 add_pos += seprintf(add_pos, last, "Trying to load sound set, but it is incomplete. The game will probably not run correctly until you properly install this set or select another one.\n\nThe following files are corrupted or missing:\n");
00261 add_pos += seprintf(add_pos, last, "\tsample.cat (You can find it on your Transport Tycoon Deluxe CD-ROM.)\n");
00262 }
00263
00264 if (add_pos != error_msg) ShowInfoF("%s", error_msg);
00265 }
00266
00267
00268 static void LoadSpriteTables()
00269 {
00270 memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf));
00271 uint i = FIRST_GRF_SLOT;
00272
00273 _palette_remap_grf[i] = (_use_palette != _used_graphics_set->palette);
00274 LoadGrfFile(_used_graphics_set->files[GFT_BASE].filename, 0, i++);
00275
00276
00277
00278
00279
00280
00281
00282 _palette_remap_grf[i] = (_use_palette != _used_graphics_set->palette);
00283 LoadGrfFile(_used_graphics_set->files[GFT_LOGOS].filename, 4793, i++);
00284
00285
00286
00287
00288
00289
00290 if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
00291 _palette_remap_grf[i] = (_use_palette != _used_graphics_set->palette);
00292 LoadGrfIndexed(
00293 _used_graphics_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
00294 _landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
00295 i++
00296 );
00297 }
00298
00299
00300 InitializeUnicodeGlyphMap();
00301
00302
00303
00304
00305
00306
00307 GRFConfig *top = _grfconfig;
00308 GRFConfig *master = CallocT<GRFConfig>(1);
00309 master->filename = strdup(_used_graphics_set->files[GFT_EXTRA].filename);
00310 FillGRFDetails(master, false);
00311 master->windows_paletted = (_used_graphics_set->palette == PAL_WINDOWS);
00312 ClrBit(master->flags, GCF_INIT_ONLY);
00313 master->next = top;
00314 _grfconfig = master;
00315
00316 LoadNewGRF(SPR_NEWGRFS_BASE, i);
00317
00318
00319 ClearGRFConfig(&master);
00320 _grfconfig = top;
00321 }
00322
00323
00324 void GfxLoadSprites()
00325 {
00326 DEBUG(sprite, 2, "Loading sprite set %d", _settings_game.game_creation.landscape);
00327
00328 GfxInitSpriteMem();
00329 LoadSpriteTables();
00330 GfxInitPalettes();
00331 }
00332
00337 #define fetch_metadata(name) \
00338 item = metadata->GetItem(name, false); \
00339 if (item == NULL || strlen(item->value) == 0) { \
00340 DEBUG(grf, 0, "Base graphics set detail loading: %s field missing", name); \
00341 return false; \
00342 }
00343
00345 static const char *_gft_names[MAX_GFT] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
00346
00354 static bool FillGraphicsSetDetails(GraphicsSet *graphics, IniFile *ini, const char *path)
00355 {
00356 memset(graphics, 0, sizeof(*graphics));
00357
00358 IniGroup *metadata = ini->GetGroup("metadata");
00359 IniItem *item;
00360
00361 fetch_metadata("name");
00362 graphics->name = strdup(item->value);
00363
00364 fetch_metadata("description");
00365 graphics->description = strdup(item->value);
00366
00367 fetch_metadata("shortname");
00368 for (uint i = 0; item->value[i] != '\0' && i < 4; i++) {
00369 graphics->shortname |= ((uint8)item->value[i]) << (i * 8);
00370 }
00371
00372 fetch_metadata("version");
00373 graphics->version = atoi(item->value);
00374
00375 fetch_metadata("palette");
00376 graphics->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
00377
00378
00379 IniGroup *files = ini->GetGroup("files");
00380 IniGroup *md5s = ini->GetGroup("md5s");
00381 IniGroup *origin = ini->GetGroup("origin");
00382 for (uint i = 0; i < MAX_GFT; i++) {
00383 MD5File *file = &graphics->files[i];
00384
00385 item = files->GetItem(_gft_names[i], false);
00386 if (item == NULL) {
00387 DEBUG(grf, 0, "No graphics file for: %s", _gft_names[i]);
00388 return false;
00389 }
00390
00391 const char *filename = item->value;
00392 file->filename = MallocT<char>(strlen(filename) + strlen(path) + 1);
00393 sprintf((char*)file->filename, "%s%s", path, filename);
00394
00395
00396 item = md5s->GetItem(filename, false);
00397 if (item == NULL) {
00398 DEBUG(grf, 0, "No MD5 checksum specified for: %s", filename);
00399 return false;
00400 }
00401 char *c = item->value;
00402 for (uint i = 0; i < sizeof(file->hash) * 2; i++, c++) {
00403 uint j;
00404 if ('0' <= *c && *c <= '9') {
00405 j = *c - '0';
00406 } else if ('a' <= *c && *c <= 'f') {
00407 j = *c - 'a' + 10;
00408 } else if ('A' <= *c && *c <= 'F') {
00409 j = *c - 'A' + 10;
00410 } else {
00411 DEBUG(grf, 0, "Malformed MD5 checksum specified for: %s", filename);
00412 return false;
00413 }
00414 if (i % 2 == 0) {
00415 file->hash[i / 2] = j << 4;
00416 } else {
00417 file->hash[i / 2] |= j;
00418 }
00419 }
00420
00421
00422 item = origin->GetItem(filename, false);
00423 if (item == NULL) item = origin->GetItem("default", false);
00424 if (item == NULL) {
00425 DEBUG(grf, 1, "No origin warning message specified for: %s", filename);
00426 file->missing_warning = strdup("");
00427 } else {
00428 file->missing_warning = strdup(item->value);
00429 }
00430
00431 if (FileMD5(*file)) graphics->found_grfs++;
00432 }
00433
00434 return true;
00435 }
00436
00438 class OBGFileScanner : FileScanner {
00439 public:
00440 bool AddFile(const char *filename, size_t basepath_length);
00441
00443 static uint DoScan()
00444 {
00445 OBGFileScanner fs;
00446 return fs.Scan(".obg", DATA_DIR);
00447 }
00448 };
00449
00456 bool OBGFileScanner::AddFile(const char *filename, size_t basepath_length)
00457 {
00458 bool ret = false;
00459 DEBUG(grf, 1, "Checking %s for base graphics set", filename);
00460
00461 GraphicsSet *graphics = new GraphicsSet();;
00462 IniFile *ini = new IniFile();
00463 ini->LoadFromDisk(filename);
00464
00465 char *path = strdup(filename + basepath_length);
00466 char *psep = strrchr(path, PATHSEPCHAR);
00467 if (psep != NULL) {
00468 psep[1] = '\0';
00469 } else {
00470 *path = '\0';
00471 }
00472
00473 if (FillGraphicsSetDetails(graphics, ini, path)) {
00474 GraphicsSet *duplicate = NULL;
00475 for (GraphicsSet *c = _available_graphics_sets; c != NULL; c = c->next) {
00476 if (strcmp(c->name, graphics->name) == 0 || c->shortname == graphics->shortname) {
00477 duplicate = c;
00478 break;
00479 }
00480 }
00481 if (duplicate != NULL) {
00482
00483 if ((duplicate->found_grfs == graphics->found_grfs && duplicate->version >= graphics->version) ||
00484 duplicate->found_grfs > graphics->found_grfs) {
00485 DEBUG(grf, 1, "Not adding %s (%i) as base graphics set (duplicate)", graphics->name, graphics->version);
00486 delete graphics;
00487 } else {
00488 GraphicsSet **prev = &_available_graphics_sets;
00489 while (*prev != duplicate) prev = &(*prev)->next;
00490
00491 *prev = graphics;
00492 graphics->next = duplicate->next;
00493
00494 duplicate->next = NULL;
00495
00496
00497
00498
00499 if (_used_graphics_set == duplicate) _used_graphics_set = graphics;
00500
00501 DEBUG(grf, 1, "Removing %s (%i) as base graphics set (duplicate)", duplicate->name, duplicate->version);
00502 delete duplicate;
00503 ret = true;
00504 }
00505 } else {
00506 GraphicsSet **last = &_available_graphics_sets;
00507 while (*last != NULL) last = &(*last)->next;
00508
00509 *last = graphics;
00510 ret = true;
00511 }
00512 if (ret) {
00513 DEBUG(grf, 1, "Adding %s (%i) as base graphics set", graphics->name, graphics->version);
00514 }
00515 } else {
00516 delete graphics;
00517 }
00518 free(path);
00519
00520 delete ini;
00521 return ret;
00522 }
00523
00524
00525
00527 void FindGraphicsSets()
00528 {
00529 DEBUG(grf, 1, "Scanning for Graphics sets");
00530 OBGFileScanner::DoScan();
00531 }
00532
00538 bool SetGraphicsSet(const char *name)
00539 {
00540 if (StrEmpty(name)) {
00541 if (!DetermineGraphicsPack()) return false;
00542 CheckExternalFiles();
00543 return true;
00544 }
00545
00546 for (const GraphicsSet *g = _available_graphics_sets; g != NULL; g = g->next) {
00547 if (strcmp(name, g->name) == 0) {
00548 _used_graphics_set = g;
00549 CheckExternalFiles();
00550 return true;
00551 }
00552 }
00553 return false;
00554 }
00555
00562 char *GetGraphicsSetsList(char *p, const char *last)
00563 {
00564 p += seprintf(p, last, "List of graphics sets:\n");
00565 for (const GraphicsSet *g = _available_graphics_sets; g != NULL; g = g->next) {
00566 if (g->found_grfs <= 1) continue;
00567
00568 p += seprintf(p, last, "%18s: %s", g->name, g->description);
00569 int difference = MAX_GFT - g->found_grfs;
00570 if (difference != 0) {
00571 p += seprintf(p, last, " (missing %i file%s)\n", difference, difference == 1 ? "" : "s");
00572 } else {
00573 p += seprintf(p, last, "\n");
00574 }
00575 }
00576 p += seprintf(p, last, "\n");
00577
00578 return p;
00579 }
00580
00581 #if defined(ENABLE_NETWORK)
00582 #include "network/network_content.h"
00583
00590 bool HasGraphicsSet(const ContentInfo *ci, bool md5sum)
00591 {
00592 assert(ci->type == CONTENT_TYPE_BASE_GRAPHICS);
00593 for (const GraphicsSet *g = _available_graphics_sets; g != NULL; g = g->next) {
00594 if (g->found_grfs <= 1) continue;
00595
00596 if (g->shortname != ci->unique_id) continue;
00597 if (!md5sum) return true;
00598
00599 byte md5[16];
00600 memset(md5, 0, sizeof(md5));
00601 for (uint i = 0; i < MAX_GFT; i++) {
00602 for (uint j = 0; j < sizeof(md5); j++) {
00603 md5[j] ^= g->files[i].hash[j];
00604 }
00605 }
00606 if (memcmp(md5, ci->md5sum, sizeof(md5)) == 0) return true;
00607 }
00608
00609 return false;
00610 }
00611
00612 #endif
00613
00617 int GetNumGraphicsSets()
00618 {
00619 int n = 0;
00620 for (const GraphicsSet *g = _available_graphics_sets; g != NULL; g = g->next) {
00621 if (g != _used_graphics_set && g->found_grfs <= 1) continue;
00622 n++;
00623 }
00624 return n;
00625 }
00626
00630 int GetIndexOfCurrentGraphicsSet()
00631 {
00632 int n = 0;
00633 for (const GraphicsSet *g = _available_graphics_sets; g != NULL; g = g->next) {
00634 if (g == _used_graphics_set) return n;
00635 if (g->found_grfs <= 1) continue;
00636 n++;
00637 }
00638 return -1;
00639 }
00640
00644 const char *GetGraphicsSetName(int index)
00645 {
00646 for (const GraphicsSet *g = _available_graphics_sets; g != NULL; g = g->next) {
00647 if (g != _used_graphics_set && g->found_grfs <= 1) continue;
00648 if (index == 0) return g->name;
00649 index--;
00650 }
00651 error("GetGraphicsSetName: index %d out of range", index);
00652 }