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/>.
8 /** @file base_media_base.h Generic functions for replacing base data (graphics, sounds). */
10 #ifndef BASE_MEDIA_BASE_H
11 #define BASE_MEDIA_BASE_H
13 #include "fileio_func.h"
15 #include "textfile_type.h"
16 #include "textfile_gui.h"
17 #include "3rdparty/md5/md5.h"
18 #include <unordered_map>
20 /* Forward declare these; can't do 'struct X' in functions as older GCCs barf on that */
24 /** Structure holding filename and MD5 information about a single file */
26 /** The result of a checksum check */
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
34 std::string filename
; ///< filename
35 MD5Hash hash
; ///< md5 sum of the file
36 std::string missing_warning
; ///< warning when this file is missing
37 ChecksumResult check_result
; ///< cached result of md5 check
39 ChecksumResult
CheckMD5(Subdirectory subdir
, size_t max_size
) const;
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
48 template <class T
, size_t Tnum_files
, bool Tsearch_in_tars
>
50 typedef std::unordered_map
<std::string
, std::string
> TranslatedStrings
;
52 /** Number of files in this set */
53 static const size_t NUM_FILES
= Tnum_files
;
55 /** Whether to search in the tars or not. */
56 static const bool SEARCH_IN_TARS
= Tsearch_in_tars
;
58 /** Internal names of the files in this set. */
59 static const char * const *file_names
;
61 std::string name
; ///< The name of the base set
62 std::string url
; ///< URL for information about the base set
63 TranslatedStrings description
; ///< Description of the base set
64 uint32_t shortname
; ///< Four letter short variant of the name
65 uint32_t version
; ///< The version of this base set
66 bool fallback
; ///< This set is a fallback set, i.e. it should be used only as last resort
68 MD5File files
[NUM_FILES
]; ///< All files part of this set
69 uint found_files
; ///< Number of the files that could be found
70 uint valid_files
; ///< Number of the files that could be found and are valid
72 T
*next
; ///< The next base set in this list
74 /** Free everything we allocated */
81 * Get the number of missing files.
84 int GetNumMissing() const
86 return Tnum_files
- this->found_files
;
90 * Get the number of invalid files.
91 * @note a missing file is invalid too!
94 int GetNumInvalid() const
96 return Tnum_files
- this->valid_files
;
99 bool FillSetDetails(const IniFile
&ini
, const std::string
&path
, const std::string
&full_filename
, bool allow_empty_filename
= true);
100 void CopyCompatibleConfig([[maybe_unused
]] const T
&src
) {}
103 * Get the description for the given ISO code.
104 * It falls back to the first two characters of the ISO code in case
105 * no match could be made with the full ISO code. If even then the
106 * matching fails the default is returned.
107 * @param isocode the isocode to search for
108 * @return the description
110 const std::string
&GetDescription(const std::string
&isocode
) const
112 if (!isocode
.empty()) {
113 /* First the full ISO code */
114 auto desc
= this->description
.find(isocode
);
115 if (desc
!= this->description
.end()) return desc
->second
;
117 /* Then the first two characters */
118 desc
= this->description
.find(isocode
.substr(0, 2));
119 if (desc
!= this->description
.end()) return desc
->second
;
122 return this->description
.at(std::string
{});
126 * Get string to use when listing this set in the settings window.
127 * If there are no invalid files, then this is just the set name,
128 * otherwise a string is formatted including the number of invalid files.
129 * @return the string to display.
131 std::string
GetListLabel() const
133 if (this->GetNumInvalid() == 0) return this->name
;
135 SetDParamStr(0, this->name
);
136 SetDParam(1, this->GetNumInvalid());
137 return GetString(STR_BASESET_STATUS
);
141 * Calculate and check the MD5 hash of the supplied file.
142 * @param file The file get the hash of.
143 * @param subdir The sub directory to get the files from.
145 * - #CR_MATCH if the MD5 hash matches
146 * - #CR_MISMATCH if the MD5 does not match
147 * - #CR_NO_FILE if the file misses
149 static MD5File::ChecksumResult
CheckMD5(const MD5File
*file
, Subdirectory subdir
)
151 return file
->CheckMD5(subdir
, SIZE_MAX
);
155 * Search a textfile file next to this base media.
156 * @param type The type of the textfile to search for.
157 * @return The filename for the textfile.
159 std::optional
<std::string
> GetTextfile(TextfileType type
) const
161 for (uint i
= 0; i
< NUM_FILES
; i
++) {
162 auto textfile
= ::GetTextfile(type
, BASESET_DIR
, this->files
[i
].filename
);
163 if (textfile
.has_value()) {
172 * Base for all base media (graphics, sounds)
173 * @tparam Tbase_set the real set we're going to be
175 template <class Tbase_set
>
176 class BaseMedia
: FileScanner
{
178 static Tbase_set
*available_sets
; ///< All available sets
179 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.
180 static const Tbase_set
*used_set
; ///< The currently used set
182 bool AddFile(const std::string
&filename
, size_t basepath_length
, const std::string
&tar_filename
) override
;
185 * Get the extension that is used to identify this set.
186 * @return the extension
188 static const char *GetExtension();
191 * Determine the graphics pack that has to be used.
192 * The one with the most correct files wins.
193 * @return true if a best set has been found.
195 static bool DetermineBestSet();
197 /** Do the scan for files. */
198 static uint
FindSets()
200 BaseMedia
<Tbase_set
> fs
;
201 /* Searching in tars is only done in the old "data" directories basesets. */
202 uint num
= fs
.Scan(GetExtension(), Tbase_set::SEARCH_IN_TARS
? OLD_DATA_DIR
: OLD_GM_DIR
, Tbase_set::SEARCH_IN_TARS
);
203 return num
+ fs
.Scan(GetExtension(), BASESET_DIR
, Tbase_set::SEARCH_IN_TARS
);
206 static Tbase_set
*GetAvailableSets();
208 static bool SetSet(const Tbase_set
*set
);
209 static bool SetSetByName(const std::string
&name
);
210 static bool SetSetByShortname(uint32_t shortname
);
211 static void GetSetsList(std::back_insert_iterator
<std::string
> &output_iterator
);
212 static int GetNumSets();
213 static int GetIndexOfUsedSet();
214 static const Tbase_set
*GetSet(int index
);
215 static const Tbase_set
*GetUsedSet();
218 * Check whether we have an set with the exact characteristics as ci.
219 * @param ci the characteristics to search on (shortname and md5sum)
220 * @param md5sum whether to check the MD5 checksum
221 * @return true iff we have an set matching.
223 static bool HasSet(const ContentInfo
*ci
, bool md5sum
);
226 template <class Tbase_set
> /* static */ const Tbase_set
*BaseMedia
<Tbase_set
>::used_set
;
227 template <class Tbase_set
> /* static */ Tbase_set
*BaseMedia
<Tbase_set
>::available_sets
;
228 template <class Tbase_set
> /* static */ Tbase_set
*BaseMedia
<Tbase_set
>::duplicate_sets
;
231 * Check whether there's a base set matching some information.
232 * @param ci The content info to compare it to.
233 * @param md5sum Should the MD5 checksum be tested as well?
234 * @param s The list with sets.
235 * @return The filename of the first file of the base set, or \c nullptr if there is no match.
237 template <class Tbase_set
>
238 const char *TryGetBaseSetFile(const ContentInfo
*ci
, bool md5sum
, const Tbase_set
*s
);
240 /** Types of graphics in the base graphics set */
241 enum GraphicsFileType
{
242 GFT_BASE
, ///< Base sprites for all climates
243 GFT_LOGOS
, ///< Logos, landscape icons and original terrain generator sprites
244 GFT_ARCTIC
, ///< Landscape replacement sprites for arctic
245 GFT_TROPICAL
, ///< Landscape replacement sprites for tropical
246 GFT_TOYLAND
, ///< Landscape replacement sprites for toyland
247 GFT_EXTRA
, ///< Extra sprites that were not part of the original sprites
248 MAX_GFT
, ///< We are looking for this amount of GRFs
251 /** Blitter type for base graphics sets. */
253 BLT_8BPP
, ///< Base set has 8 bpp sprites only.
254 BLT_32BPP
, ///< Base set has both 8 bpp and 32 bpp sprites.
259 /** All data of a graphics set. */
260 struct GraphicsSet
: BaseSet
<GraphicsSet
, MAX_GFT
, true> {
262 mutable std::unique_ptr
<GRFConfig
> extra_cfg
; ///< Parameters for extra GRF
264 PaletteType palette
; ///< Palette of this graphics set
265 BlitterType blitter
; ///< Blitter of this graphics set
270 bool FillSetDetails(const IniFile
&ini
, const std::string
&path
, const std::string
&full_filename
);
271 GRFConfig
*GetExtraConfig() const { return this->extra_cfg
.get(); }
272 GRFConfig
&GetOrCreateExtraConfig() const;
273 bool IsConfigurable() const;
274 void CopyCompatibleConfig(const GraphicsSet
&src
);
276 static MD5File::ChecksumResult
CheckMD5(const MD5File
*file
, Subdirectory subdir
);
279 /** All data/functions related with replacing the base graphics. */
280 class BaseGraphics
: public BaseMedia
<GraphicsSet
> {
282 /** Values loaded from config file. */
285 uint32_t shortname
; ///< unique key for base set
286 uint32_t extra_version
; ///< version of the extra GRF
287 std::vector
<uint32_t> extra_params
; ///< parameters for the extra GRF
289 static inline Ini ini_data
;
293 /** All data of a sounds set. */
294 struct SoundsSet
: BaseSet
<SoundsSet
, 1, true> {
297 /** All data/functions related with replacing the base sounds */
298 class BaseSounds
: public BaseMedia
<SoundsSet
> {
300 /** The set as saved in the config file. */
301 static inline std::string ini_set
;
305 /** Maximum number of songs in the 'class' playlists. */
306 static const uint NUM_SONGS_CLASS
= 10;
307 /** Number of classes for songs */
308 static const uint NUM_SONG_CLASSES
= 3;
309 /** Maximum number of songs in the full playlist; theme song + the classes */
310 static const uint NUM_SONGS_AVAILABLE
= 1 + NUM_SONG_CLASSES
* NUM_SONGS_CLASS
;
312 /** Maximum number of songs in the (custom) playlist */
313 static const uint NUM_SONGS_PLAYLIST
= 32;
315 /* Functions to read DOS music CAT files, similar to but not quite the same as sound effect CAT files */
316 char *GetMusicCatEntryName(const std::string
&filename
, size_t entrynum
);
317 byte
*GetMusicCatEntryData(const std::string
&filename
, size_t entrynum
, size_t &entrylen
);
319 enum MusicTrackType
{
320 MTT_STANDARDMIDI
, ///< Standard MIDI file
321 MTT_MPSMIDI
, ///< MPS GM driver MIDI format (contained in a CAT file)
324 /** Metadata about a music track. */
325 struct MusicSongInfo
{
326 std::string songname
; ///< name of song displayed in UI
327 byte tracknr
; ///< track number of song displayed in UI
328 std::string filename
; ///< file on disk containing song (when used in MusicSet class)
329 MusicTrackType filetype
; ///< decoder required for song file
330 int cat_index
; ///< entry index in CAT file, for filetype==MTT_MPSMIDI
331 bool loop
; ///< song should play in a tight loop if possible, never ending
332 int override_start
; ///< MIDI ticks to skip over in beginning
333 int override_end
; ///< MIDI tick to end the song at (0 if no override)
336 /** All data of a music set. */
337 struct MusicSet
: BaseSet
<MusicSet
, NUM_SONGS_AVAILABLE
, false> {
338 /** Data about individual songs in set. */
339 MusicSongInfo songinfo
[NUM_SONGS_AVAILABLE
];
340 /** Number of valid songs in set. */
343 bool FillSetDetails(const IniFile
&ini
, const std::string
&path
, const std::string
&full_filename
);
346 /** All data/functions related with replacing the base music */
347 class BaseMusic
: public BaseMedia
<MusicSet
> {
349 /** The set as saved in the config file. */
350 static inline std::string ini_set
;
354 #endif /* BASE_MEDIA_BASE_H */