4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file music.cpp The songs that OpenTTD knows. */
15 /** The type of set we're replacing */
16 #define SET_TYPE "music"
17 #include "base_media_func.h"
19 #include "safeguards.h"
24 * Read the name of a music CAT file entry.
25 * @param filename Name of CAT file to read from
26 * @param entrynum Index of entry whose name to read
27 * @return Pointer to string, caller is responsible for freeing memory,
28 * NULL if entrynum does not exist.
30 char *GetMusicCatEntryName(const char *filename
, size_t entrynum
)
32 if (!FioCheckFileExists(filename
, BASESET_DIR
)) return NULL
;
34 FioOpenFile(CONFIG_SLOT
, filename
, BASESET_DIR
);
35 uint32 ofs
= FioReadDword();
36 size_t entry_count
= ofs
/ 8;
37 if (entrynum
< entry_count
) {
38 FioSeekTo(entrynum
* 8, SEEK_SET
);
39 FioSeekTo(FioReadDword(), SEEK_SET
);
40 byte namelen
= FioReadByte();
41 char *name
= MallocT
<char>(namelen
+ 1);
42 FioReadBlock(name
, namelen
);
50 * Read the full data of a music CAT file entry.
51 * @param filename Name of CAT file to read from.
52 * @param entrynum Index of entry to read
53 * @param[out] entrylen Receives length of data read
54 * @return Pointer to buffer with data read, caller is responsible for freeind memory,
55 * NULL if entrynum does not exist.
57 byte
*GetMusicCatEntryData(const char *filename
, size_t entrynum
, size_t &entrylen
)
60 if (!FioCheckFileExists(filename
, BASESET_DIR
)) return NULL
;
62 FioOpenFile(CONFIG_SLOT
, filename
, BASESET_DIR
);
63 uint32 ofs
= FioReadDword();
64 size_t entry_count
= ofs
/ 8;
65 if (entrynum
< entry_count
) {
66 FioSeekTo(entrynum
* 8, SEEK_SET
);
67 size_t entrypos
= FioReadDword();
68 entrylen
= FioReadDword();
69 FioSeekTo(entrypos
, SEEK_SET
);
70 FioSkipBytes(FioReadByte());
71 byte
*data
= MallocT
<byte
>(entrylen
);
72 FioReadBlock(data
, entrylen
);
78 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia
<MusicSet
>, MusicSet
)
80 /** Names corresponding to the music set's files */
81 static const char * const _music_file_names
[] = {
83 "old_0", "old_1", "old_2", "old_3", "old_4", "old_5", "old_6", "old_7", "old_8", "old_9",
84 "new_0", "new_1", "new_2", "new_3", "new_4", "new_5", "new_6", "new_7", "new_8", "new_9",
85 "ezy_0", "ezy_1", "ezy_2", "ezy_3", "ezy_4", "ezy_5", "ezy_6", "ezy_7", "ezy_8", "ezy_9",
87 /** Make sure we aren't messing things up. */
88 assert_compile(lengthof(_music_file_names
) == NUM_SONGS_AVAILABLE
);
90 template <class T
, size_t Tnum_files
, bool Tsearch_in_tars
>
91 /* static */ const char * const *BaseSet
<T
, Tnum_files
, Tsearch_in_tars
>::file_names
= _music_file_names
;
93 template <class Tbase_set
>
94 /* static */ const char *BaseMedia
<Tbase_set
>::GetExtension()
96 return ".obm"; // OpenTTD Base Music
99 template <class Tbase_set
>
100 /* static */ bool BaseMedia
<Tbase_set
>::DetermineBestSet()
102 if (BaseMedia
<Tbase_set
>::used_set
!= NULL
) return true;
104 const Tbase_set
*best
= NULL
;
105 for (const Tbase_set
*c
= BaseMedia
<Tbase_set
>::available_sets
; c
!= NULL
; c
= c
->next
) {
106 if (c
->GetNumMissing() != 0) continue;
109 (best
->fallback
&& !c
->fallback
) ||
110 best
->valid_files
< c
->valid_files
||
111 (best
->valid_files
== c
->valid_files
&&
112 (best
->shortname
== c
->shortname
&& best
->version
< c
->version
))) {
117 BaseMedia
<Tbase_set
>::used_set
= best
;
118 return BaseMedia
<Tbase_set
>::used_set
!= NULL
;
121 bool MusicSet::FillSetDetails(IniFile
*ini
, const char *path
, const char *full_filename
)
123 bool ret
= this->BaseSet
<MusicSet
, NUM_SONGS_AVAILABLE
, false>::FillSetDetails(ini
, path
, full_filename
);
125 this->num_available
= 0;
126 IniGroup
*names
= ini
->GetGroup("names");
127 IniGroup
*catindex
= ini
->GetGroup("catindex");
128 IniGroup
*timingtrim
= ini
->GetGroup("timingtrim");
130 for (uint i
= 0; i
< lengthof(this->songinfo
); i
++) {
131 const char *filename
= this->files
[i
].filename
;
132 if (names
== NULL
|| StrEmpty(filename
) || this->files
[i
].check_result
== MD5File::CR_NO_FILE
) {
133 this->songinfo
[i
].songname
[0] = '\0';
137 this->songinfo
[i
].filename
= filename
; // non-owned pointer
139 IniItem
*item
= catindex
->GetItem(_music_file_names
[i
], false);
140 if (item
!= NULL
&& !StrEmpty(item
->value
)) {
141 /* Song has a CAT file index, assume it's MPS MIDI format */
142 this->songinfo
[i
].filetype
= MTT_MPSMIDI
;
143 this->songinfo
[i
].cat_index
= atoi(item
->value
);
144 char *songname
= GetMusicCatEntryName(filename
, this->songinfo
[i
].cat_index
);
145 if (songname
== NULL
) {
146 DEBUG(grf
, 0, "Base music set song missing from CAT file: %s/%d", filename
, this->songinfo
[i
].cat_index
);
147 this->songinfo
[i
].songname
[0] = '\0';
150 strecpy(this->songinfo
[i
].songname
, songname
, lastof(this->songinfo
[i
].songname
));
153 this->songinfo
[i
].filetype
= MTT_STANDARDMIDI
;
156 const char *trimmed_filename
= filename
;
157 /* As we possibly add a path to the filename and we compare
158 * on the filename with the path as in the .obm, we need to
159 * keep stripping path elements until we find a match. */
160 for (; trimmed_filename
!= NULL
; trimmed_filename
= strchr(trimmed_filename
, PATHSEPCHAR
)) {
161 /* Remove possible double path separator characters from
162 * the beginning, so we don't start reading e.g. root. */
163 while (*trimmed_filename
== PATHSEPCHAR
) trimmed_filename
++;
165 item
= names
->GetItem(trimmed_filename
, false);
166 if (item
!= NULL
&& !StrEmpty(item
->value
)) break;
169 if (this->songinfo
[i
].filetype
== MTT_STANDARDMIDI
) {
170 if (item
!= NULL
&& !StrEmpty(item
->value
)) {
171 strecpy(this->songinfo
[i
].songname
, item
->value
, lastof(this->songinfo
[i
].songname
));
173 DEBUG(grf
, 0, "Base music set song name missing: %s", filename
);
177 this->num_available
++;
179 /* Number the theme song (if any) track 0, rest are normal */
181 this->songinfo
[i
].tracknr
= 0;
183 this->songinfo
[i
].tracknr
= tracknr
++;
186 item
= timingtrim
->GetItem(trimmed_filename
, false);
187 if (item
!= NULL
&& !StrEmpty(item
->value
)) {
188 const char *endpos
= strchr(item
->value
, ':');
189 if (endpos
!= NULL
) {
190 this->songinfo
[i
].override_start
= atoi(item
->value
);
191 this->songinfo
[i
].override_end
= atoi(endpos
+ 1);