Update: Translations from eints
[openttd-github.git] / src / music.cpp
blob52a8ef259cf31b5120511c10573c47080acf95ca
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 */
8 /** @file music.cpp The songs that OpenTTD knows. */
10 #include "stdafx.h"
11 #include "string_func.h"
14 /** The type of set we're replacing */
15 #define SET_TYPE "music"
16 #include "base_media_func.h"
17 #include "random_access_file_type.h"
19 #include "safeguards.h"
22 /**
23 * Read the name of a music CAT file entry.
24 * @param filename Name of CAT file to read from
25 * @param entrynum Index of entry whose name to read
26 * @return Name of CAT file entry if it could be read.
28 std::optional<std::string> GetMusicCatEntryName(const std::string &filename, size_t entrynum)
30 if (!FioCheckFileExists(filename, BASESET_DIR)) return std::nullopt;
32 RandomAccessFile file(filename, BASESET_DIR);
33 uint32_t ofs = file.ReadDword();
34 size_t entry_count = ofs / 8;
35 if (entrynum >= entry_count) return std::nullopt;
37 file.SeekTo(entrynum * 8, SEEK_SET);
38 file.SeekTo(file.ReadDword(), SEEK_SET);
39 uint8_t namelen = file.ReadByte();
41 std::string name(namelen, '\0');
42 file.ReadBlock(name.data(), namelen);
43 return StrMakeValid(name);
46 /**
47 * Read the full data of a music CAT file entry.
48 * @param filename Name of CAT file to read from.
49 * @param entrynum Index of entry to read
50 * @return Data of CAT file entry.
52 std::optional<std::vector<uint8_t>> GetMusicCatEntryData(const std::string &filename, size_t entrynum)
54 if (!FioCheckFileExists(filename, BASESET_DIR)) return std::nullopt;
56 RandomAccessFile file(filename, BASESET_DIR);
57 uint32_t ofs = file.ReadDword();
58 size_t entry_count = ofs / 8;
59 if (entrynum >= entry_count) return std::nullopt;
61 file.SeekTo(entrynum * 8, SEEK_SET);
62 size_t entrypos = file.ReadDword();
63 size_t entrylen = file.ReadDword();
64 file.SeekTo(entrypos, SEEK_SET);
65 file.SkipBytes(file.ReadByte());
67 std::vector<uint8_t> data(entrylen);
68 file.ReadBlock(data.data(), entrylen);
69 return data;
72 INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<MusicSet>, MusicSet)
74 /** Names corresponding to the music set's files */
75 static const char * const _music_file_names[] = {
76 "theme",
77 "old_0", "old_1", "old_2", "old_3", "old_4", "old_5", "old_6", "old_7", "old_8", "old_9",
78 "new_0", "new_1", "new_2", "new_3", "new_4", "new_5", "new_6", "new_7", "new_8", "new_9",
79 "ezy_0", "ezy_1", "ezy_2", "ezy_3", "ezy_4", "ezy_5", "ezy_6", "ezy_7", "ezy_8", "ezy_9",
81 /** Make sure we aren't messing things up. */
82 static_assert(lengthof(_music_file_names) == NUM_SONGS_AVAILABLE);
84 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
85 /* static */ const char * const *BaseSet<T, Tnum_files, Tsearch_in_tars>::file_names = _music_file_names;
87 template <class Tbase_set>
88 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
90 return ".obm"; // OpenTTD Base Music
93 template <class Tbase_set>
94 /* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
96 if (BaseMedia<Tbase_set>::used_set != nullptr) return true;
98 const Tbase_set *best = nullptr;
99 for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != nullptr; c = c->next) {
100 if (c->GetNumMissing() != 0) continue;
102 if (best == nullptr ||
103 (best->fallback && !c->fallback) ||
104 best->valid_files < c->valid_files ||
105 (best->valid_files == c->valid_files &&
106 (best->shortname == c->shortname && best->version < c->version))) {
107 best = c;
111 BaseMedia<Tbase_set>::used_set = best;
112 return BaseMedia<Tbase_set>::used_set != nullptr;
115 bool MusicSet::FillSetDetails(const IniFile &ini, const std::string &path, const std::string &full_filename)
117 bool ret = this->BaseSet<MusicSet, NUM_SONGS_AVAILABLE, false>::FillSetDetails(ini, path, full_filename);
118 if (ret) {
119 this->num_available = 0;
120 const IniGroup *names = ini.GetGroup("names");
121 const IniGroup *catindex = ini.GetGroup("catindex");
122 const IniGroup *timingtrim = ini.GetGroup("timingtrim");
123 uint tracknr = 1;
124 for (uint i = 0; i < lengthof(this->songinfo); i++) {
125 const std::string &filename = this->files[i].filename;
126 if (filename.empty() || this->files[i].check_result == MD5File::CR_NO_FILE) {
127 continue;
130 this->songinfo[i].filename = filename; // non-owned pointer
132 const IniItem *item = catindex != nullptr ? catindex->GetItem(_music_file_names[i]) : nullptr;
133 if (item != nullptr && item->value.has_value() && !item->value->empty()) {
134 /* Song has a CAT file index, assume it's MPS MIDI format */
135 this->songinfo[i].filetype = MTT_MPSMIDI;
136 this->songinfo[i].cat_index = atoi(item->value->c_str());
137 auto songname = GetMusicCatEntryName(filename, this->songinfo[i].cat_index);
138 if (!songname.has_value()) {
139 Debug(grf, 0, "Base music set song missing from CAT file: {}/{}", filename, this->songinfo[i].cat_index);
140 continue;
142 this->songinfo[i].songname = *songname;
143 } else {
144 this->songinfo[i].filetype = MTT_STANDARDMIDI;
147 const char *trimmed_filename = filename.c_str();
148 /* As we possibly add a path to the filename and we compare
149 * on the filename with the path as in the .obm, we need to
150 * keep stripping path elements until we find a match. */
151 for (; trimmed_filename != nullptr; trimmed_filename = strchr(trimmed_filename, PATHSEPCHAR)) {
152 /* Remove possible double path separator characters from
153 * the beginning, so we don't start reading e.g. root. */
154 while (*trimmed_filename == PATHSEPCHAR) trimmed_filename++;
156 item = names != nullptr ? names->GetItem(trimmed_filename) : nullptr;
157 if (item != nullptr && item->value.has_value() && !item->value->empty()) break;
160 if (this->songinfo[i].filetype == MTT_STANDARDMIDI) {
161 if (item != nullptr && item->value.has_value() && !item->value->empty()) {
162 this->songinfo[i].songname = item->value.value();
163 } else {
164 Debug(grf, 0, "Base music set song name missing: {}", filename);
165 return false;
168 this->num_available++;
170 /* Number the theme song (if any) track 0, rest are normal */
171 if (i == 0) {
172 this->songinfo[i].tracknr = 0;
173 } else {
174 this->songinfo[i].tracknr = tracknr++;
177 item = trimmed_filename != nullptr && timingtrim != nullptr ? timingtrim->GetItem(trimmed_filename) : nullptr;
178 if (item != nullptr && item->value.has_value() && !item->value->empty()) {
179 auto endpos = item->value->find(':');
180 if (endpos != std::string::npos) {
181 this->songinfo[i].override_start = atoi(item->value->c_str());
182 this->songinfo[i].override_end = atoi(item->value->c_str() + endpos + 1);
187 return ret;