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 base_media_base.h Generic functions for replacing base data (graphics, sounds). */ 9 10 #ifndef BASE_MEDIA_BASE_H 11 #define BASE_MEDIA_BASE_H 12 13 #include "fileio_func.h" 14 #include "core/smallmap_type.hpp" 15 #include "gfx_type.h" 16 #include "textfile_type.h" 17 #include "textfile_gui.h" 18 #include <unordered_map> 19 20 /* Forward declare these; can't do 'struct X' in functions as older GCCs barf on that */ 21 struct IniFile; 22 struct ContentInfo; 23 24 /** Structure holding filename and MD5 information about a single file */ 25 struct MD5File { 26 /** The result of a checksum check */ 27 enum ChecksumResult { 28 CR_UNKNOWN, ///< The file has not been checked yet 29 CR_MATCH, ///< The file did exist and the md5 checksum did match 30 CR_MISMATCH, ///< The file did exist, just the md5 checksum did not match 31 CR_NO_FILE, ///< The file did not exist 32 }; 33 34 const char *filename; ///< filename 35 uint8 hash[16]; ///< md5 sum of the file 36 const char *missing_warning; ///< warning when this file is missing 37 ChecksumResult check_result; ///< cached result of md5 check 38 39 ChecksumResult CheckMD5(Subdirectory subdir, size_t max_size) const; 40 }; 41 42 /** 43 * Information about a single base set. 44 * @tparam T the real class we're going to be 45 * @tparam Tnum_files the number of files in the set 46 * @tparam Tsearch_in_tars whether to search in the tars or not 47 */ 48 template <class T, size_t Tnum_files, bool Tsearch_in_tars> 49 struct BaseSet { 50 typedef std::unordered_map<std::string, std::string> TranslatedStrings; 51 52 /** Number of files in this set */ 53 static const size_t NUM_FILES = Tnum_files; 54 55 /** Whether to search in the tars or not. */ 56 static const bool SEARCH_IN_TARS = Tsearch_in_tars; 57 58 /** Internal names of the files in this set. */ 59 static const char * const *file_names; 60 61 std::string name; ///< The name of the base set 62 TranslatedStrings description; ///< Description of the base set 63 uint32 shortname; ///< Four letter short variant of the name 64 uint32 version; ///< The version of this base set 65 bool fallback; ///< This set is a fallback set, i.e. it should be used only as last resort 66 67 MD5File files[NUM_FILES]; ///< All files part of this set 68 uint found_files; ///< Number of the files that could be found 69 uint valid_files; ///< Number of the files that could be found and are valid 70 71 T *next; ///< The next base set in this list 72 73 /** Free everything we allocated */ ~BaseSetBaseSet74 ~BaseSet() 75 { 76 for (uint i = 0; i < NUM_FILES; i++) { 77 free(this->files[i].filename); 78 free(this->files[i].missing_warning); 79 } 80 81 delete this->next; 82 } 83 84 /** 85 * Get the number of missing files. 86 * @return the number 87 */ GetNumMissingBaseSet88 int GetNumMissing() const 89 { 90 return Tnum_files - this->found_files; 91 } 92 93 /** 94 * Get the number of invalid files. 95 * @note a missing file is invalid too! 96 * @return the number 97 */ GetNumInvalidBaseSet98 int GetNumInvalid() const 99 { 100 return Tnum_files - this->valid_files; 101 } 102 103 bool FillSetDetails(IniFile *ini, const char *path, const char *full_filename, bool allow_empty_filename = true); 104 105 /** 106 * Get the description for the given ISO code. 107 * It falls back to the first two characters of the ISO code in case 108 * no match could be made with the full ISO code. If even then the 109 * matching fails the default is returned. 110 * @param isocode the isocode to search for 111 * @return the description 112 */ GetDescriptionBaseSet113 const char *GetDescription(const std::string &isocode) const 114 { 115 if (!isocode.empty()) { 116 /* First the full ISO code */ 117 auto desc = this->description.find(isocode); 118 if (desc != this->description.end()) return desc->second.c_str(); 119 120 /* Then the first two characters */ 121 desc = this->description.find(isocode.substr(0, 2)); 122 if (desc != this->description.end()) return desc->second.c_str(); 123 } 124 /* Then fall back */ 125 return this->description.at(std::string{}).c_str(); 126 } 127 128 /** 129 * Calculate and check the MD5 hash of the supplied file. 130 * @param file The file get the hash of. 131 * @param subdir The sub directory to get the files from. 132 * @return 133 * - #CR_MATCH if the MD5 hash matches 134 * - #CR_MISMATCH if the MD5 does not match 135 * - #CR_NO_FILE if the file misses 136 */ CheckMD5BaseSet137 static MD5File::ChecksumResult CheckMD5(const MD5File *file, Subdirectory subdir) 138 { 139 return file->CheckMD5(subdir, SIZE_MAX); 140 } 141 142 /** 143 * Search a textfile file next to this base media. 144 * @param type The type of the textfile to search for. 145 * @return The filename for the textfile, \c nullptr otherwise. 146 */ GetTextfileBaseSet147 const char *GetTextfile(TextfileType type) const 148 { 149 for (uint i = 0; i < NUM_FILES; i++) { 150 const char *textfile = ::GetTextfile(type, BASESET_DIR, this->files[i].filename); 151 if (textfile != nullptr) { 152 return textfile; 153 } 154 } 155 return nullptr; 156 } 157 }; 158 159 /** 160 * Base for all base media (graphics, sounds) 161 * @tparam Tbase_set the real set we're going to be 162 */ 163 template <class Tbase_set> 164 class BaseMedia : FileScanner { 165 protected: 166 static Tbase_set *available_sets; ///< All available sets 167 static Tbase_set *duplicate_sets; ///< All sets that aren't available, but needed for not downloading base sets when a newer version than the one on BaNaNaS is loaded. 168 static const Tbase_set *used_set; ///< The currently used set 169 170 bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename) override; 171 172 /** 173 * Get the extension that is used to identify this set. 174 * @return the extension 175 */ 176 static const char *GetExtension(); 177 public: 178 /** The set as saved in the config file. */ 179 static std::string ini_set; 180 181 /** 182 * Determine the graphics pack that has to be used. 183 * The one with the most correct files wins. 184 * @return true if a best set has been found. 185 */ 186 static bool DetermineBestSet(); 187 188 /** Do the scan for files. */ FindSets()189 static uint FindSets() 190 { 191 BaseMedia<Tbase_set> fs; 192 /* Searching in tars is only done in the old "data" directories basesets. */ 193 uint num = fs.Scan(GetExtension(), Tbase_set::SEARCH_IN_TARS ? OLD_DATA_DIR : OLD_GM_DIR, Tbase_set::SEARCH_IN_TARS); 194 return num + fs.Scan(GetExtension(), BASESET_DIR, Tbase_set::SEARCH_IN_TARS); 195 } 196 197 static Tbase_set *GetAvailableSets(); 198 199 static bool SetSet(const std::string &name); 200 static char *GetSetsList(char *p, const char *last); 201 static int GetNumSets(); 202 static int GetIndexOfUsedSet(); 203 static const Tbase_set *GetSet(int index); 204 static const Tbase_set *GetUsedSet(); 205 206 /** 207 * Check whether we have an set with the exact characteristics as ci. 208 * @param ci the characteristics to search on (shortname and md5sum) 209 * @param md5sum whether to check the MD5 checksum 210 * @return true iff we have an set matching. 211 */ 212 static bool HasSet(const ContentInfo *ci, bool md5sum); 213 }; 214 215 template <class Tbase_set> /* static */ std::string BaseMedia<Tbase_set>::ini_set; 216 template <class Tbase_set> /* static */ const Tbase_set *BaseMedia<Tbase_set>::used_set; 217 template <class Tbase_set> /* static */ Tbase_set *BaseMedia<Tbase_set>::available_sets; 218 template <class Tbase_set> /* static */ Tbase_set *BaseMedia<Tbase_set>::duplicate_sets; 219 220 /** 221 * Check whether there's a base set matching some information. 222 * @param ci The content info to compare it to. 223 * @param md5sum Should the MD5 checksum be tested as well? 224 * @param s The list with sets. 225 * @return The filename of the first file of the base set, or \c nullptr if there is no match. 226 */ 227 template <class Tbase_set> 228 const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const Tbase_set *s); 229 230 /** Types of graphics in the base graphics set */ 231 enum GraphicsFileType { 232 GFT_BASE, ///< Base sprites for all climates 233 GFT_LOGOS, ///< Logos, landscape icons and original terrain generator sprites 234 GFT_ARCTIC, ///< Landscape replacement sprites for arctic 235 GFT_TROPICAL, ///< Landscape replacement sprites for tropical 236 GFT_TOYLAND, ///< Landscape replacement sprites for toyland 237 GFT_EXTRA, ///< Extra sprites that were not part of the original sprites 238 MAX_GFT, ///< We are looking for this amount of GRFs 239 }; 240 241 /** Blitter type for base graphics sets. */ 242 enum BlitterType { 243 BLT_8BPP, ///< Base set has 8 bpp sprites only. 244 BLT_32BPP, ///< Base set has both 8 bpp and 32 bpp sprites. 245 }; 246 247 /** All data of a graphics set. */ 248 struct GraphicsSet : BaseSet<GraphicsSet, MAX_GFT, true> { 249 PaletteType palette; ///< Palette of this graphics set 250 BlitterType blitter; ///< Blitter of this graphics set 251 252 bool FillSetDetails(struct IniFile *ini, const char *path, const char *full_filename); 253 254 static MD5File::ChecksumResult CheckMD5(const MD5File *file, Subdirectory subdir); 255 }; 256 257 /** All data/functions related with replacing the base graphics. */ 258 class BaseGraphics : public BaseMedia<GraphicsSet> { 259 public: 260 }; 261 262 /** All data of a sounds set. */ 263 struct SoundsSet : BaseSet<SoundsSet, 1, true> { 264 }; 265 266 /** All data/functions related with replacing the base sounds */ 267 class BaseSounds : public BaseMedia<SoundsSet> { 268 public: 269 }; 270 271 /** Maximum number of songs in the 'class' playlists. */ 272 static const uint NUM_SONGS_CLASS = 10; 273 /** Number of classes for songs */ 274 static const uint NUM_SONG_CLASSES = 3; 275 /** Maximum number of songs in the full playlist; theme song + the classes */ 276 static const uint NUM_SONGS_AVAILABLE = 1 + NUM_SONG_CLASSES * NUM_SONGS_CLASS; 277 278 /** Maximum number of songs in the (custom) playlist */ 279 static const uint NUM_SONGS_PLAYLIST = 32; 280 281 /* Functions to read DOS music CAT files, similar to but not quite the same as sound effect CAT files */ 282 char *GetMusicCatEntryName(const char *filename, size_t entrynum); 283 byte *GetMusicCatEntryData(const char *filename, size_t entrynum, size_t &entrylen); 284 285 enum MusicTrackType { 286 MTT_STANDARDMIDI, ///< Standard MIDI file 287 MTT_MPSMIDI, ///< MPS GM driver MIDI format (contained in a CAT file) 288 }; 289 290 /** Metadata about a music track. */ 291 struct MusicSongInfo { 292 char songname[32]; ///< name of song displayed in UI 293 byte tracknr; ///< track number of song displayed in UI 294 const char *filename; ///< file on disk containing song (when used in MusicSet class, this pointer is owned by MD5File object for the file) 295 MusicTrackType filetype; ///< decoder required for song file 296 int cat_index; ///< entry index in CAT file, for filetype==MTT_MPSMIDI 297 bool loop; ///< song should play in a tight loop if possible, never ending 298 int override_start; ///< MIDI ticks to skip over in beginning 299 int override_end; ///< MIDI tick to end the song at (0 if no override) 300 }; 301 302 /** All data of a music set. */ 303 struct MusicSet : BaseSet<MusicSet, NUM_SONGS_AVAILABLE, false> { 304 /** Data about individual songs in set. */ 305 MusicSongInfo songinfo[NUM_SONGS_AVAILABLE]; 306 /** Number of valid songs in set. */ 307 byte num_available; 308 309 bool FillSetDetails(struct IniFile *ini, const char *path, const char *full_filename); 310 }; 311 312 /** All data/functions related with replacing the base music */ 313 class BaseMusic : public BaseMedia<MusicSet> { 314 public: 315 }; 316 317 #endif /* BASE_MEDIA_BASE_H */ 318