OpenTTD Source  12.0-beta2
music.cpp
Go to the documentation of this file.
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 
10 #include "stdafx.h"
11 
12 
14 #define SET_TYPE "music"
15 #include "base_media_func.h"
16 
17 #include "safeguards.h"
19 
20 
28 char *GetMusicCatEntryName(const char *filename, size_t entrynum)
29 {
30  if (!FioCheckFileExists(filename, BASESET_DIR)) return nullptr;
31 
32  RandomAccessFile file(filename, BASESET_DIR);
33  uint32 ofs = file.ReadDword();
34  size_t entry_count = ofs / 8;
35  if (entrynum < entry_count) {
36  file.SeekTo(entrynum * 8, SEEK_SET);
37  file.SeekTo(file.ReadDword(), SEEK_SET);
38  byte namelen = file.ReadByte();
39  char *name = MallocT<char>(namelen + 1);
40  file.ReadBlock(name, namelen);
41  name[namelen] = '\0';
42  return name;
43  }
44  return nullptr;
45 }
46 
55 byte *GetMusicCatEntryData(const char *filename, size_t entrynum, size_t &entrylen)
56 {
57  entrylen = 0;
58  if (!FioCheckFileExists(filename, BASESET_DIR)) return nullptr;
59 
60  RandomAccessFile file(filename, BASESET_DIR);
61  uint32 ofs = file.ReadDword();
62  size_t entry_count = ofs / 8;
63  if (entrynum < entry_count) {
64  file.SeekTo(entrynum * 8, SEEK_SET);
65  size_t entrypos = file.ReadDword();
66  entrylen = file.ReadDword();
67  file.SeekTo(entrypos, SEEK_SET);
68  file.SkipBytes(file.ReadByte());
69  byte *data = MallocT<byte>(entrylen);
70  file.ReadBlock(data, entrylen);
71  return data;
72  }
73  return nullptr;
74 }
75 
77 
78 
79 static const char * const _music_file_names[] = {
80  "theme",
81  "old_0", "old_1", "old_2", "old_3", "old_4", "old_5", "old_6", "old_7", "old_8", "old_9",
82  "new_0", "new_1", "new_2", "new_3", "new_4", "new_5", "new_6", "new_7", "new_8", "new_9",
83  "ezy_0", "ezy_1", "ezy_2", "ezy_3", "ezy_4", "ezy_5", "ezy_6", "ezy_7", "ezy_8", "ezy_9",
84 };
87 
88 template <class T, size_t Tnum_files, bool Tsearch_in_tars>
90 
91 template <class Tbase_set>
92 /* static */ const char *BaseMedia<Tbase_set>::GetExtension()
93 {
94  return ".obm"; // OpenTTD Base Music
95 }
96 
97 template <class Tbase_set>
99 {
100  if (BaseMedia<Tbase_set>::used_set != nullptr) return true;
101 
102  const Tbase_set *best = nullptr;
103  for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != nullptr; c = c->next) {
104  if (c->GetNumMissing() != 0) continue;
105 
106  if (best == nullptr ||
107  (best->fallback && !c->fallback) ||
108  best->valid_files < c->valid_files ||
109  (best->valid_files == c->valid_files &&
110  (best->shortname == c->shortname && best->version < c->version))) {
111  best = c;
112  }
113  }
114 
116  return BaseMedia<Tbase_set>::used_set != nullptr;
117 }
118 
119 bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename)
120 {
121  bool ret = this->BaseSet<MusicSet, NUM_SONGS_AVAILABLE, false>::FillSetDetails(ini, path, full_filename);
122  if (ret) {
123  this->num_available = 0;
124  IniGroup *names = ini->GetGroup("names");
125  IniGroup *catindex = ini->GetGroup("catindex");
126  IniGroup *timingtrim = ini->GetGroup("timingtrim");
127  uint tracknr = 1;
128  for (uint i = 0; i < lengthof(this->songinfo); i++) {
129  const char *filename = this->files[i].filename;
130  if (names == nullptr || StrEmpty(filename) || this->files[i].check_result == MD5File::CR_NO_FILE) {
131  this->songinfo[i].songname[0] = '\0';
132  continue;
133  }
134 
135  this->songinfo[i].filename = filename; // non-owned pointer
136 
137  IniItem *item = catindex->GetItem(_music_file_names[i], false);
138  if (item != nullptr && item->value.has_value() && !item->value->empty()) {
139  /* Song has a CAT file index, assume it's MPS MIDI format */
140  this->songinfo[i].filetype = MTT_MPSMIDI;
141  this->songinfo[i].cat_index = atoi(item->value->c_str());
142  char *songname = GetMusicCatEntryName(filename, this->songinfo[i].cat_index);
143  if (songname == nullptr) {
144  Debug(grf, 0, "Base music set song missing from CAT file: {}/{}", filename, this->songinfo[i].cat_index);
145  this->songinfo[i].songname[0] = '\0';
146  continue;
147  }
148  strecpy(this->songinfo[i].songname, songname, lastof(this->songinfo[i].songname));
149  free(songname);
150  } else {
152  }
153 
154  const char *trimmed_filename = filename;
155  /* As we possibly add a path to the filename and we compare
156  * on the filename with the path as in the .obm, we need to
157  * keep stripping path elements until we find a match. */
158  for (; trimmed_filename != nullptr; trimmed_filename = strchr(trimmed_filename, PATHSEPCHAR)) {
159  /* Remove possible double path separator characters from
160  * the beginning, so we don't start reading e.g. root. */
161  while (*trimmed_filename == PATHSEPCHAR) trimmed_filename++;
162 
163  item = names->GetItem(trimmed_filename, false);
164  if (item != nullptr && item->value.has_value() && !item->value->empty()) break;
165  }
166 
167  if (this->songinfo[i].filetype == MTT_STANDARDMIDI) {
168  if (item != nullptr && item->value.has_value() && !item->value->empty()) {
169  strecpy(this->songinfo[i].songname, item->value->c_str(), lastof(this->songinfo[i].songname));
170  } else {
171  Debug(grf, 0, "Base music set song name missing: {}", filename);
172  return false;
173  }
174  }
175  this->num_available++;
176 
177  /* Number the theme song (if any) track 0, rest are normal */
178  if (i == 0) {
179  this->songinfo[i].tracknr = 0;
180  } else {
181  this->songinfo[i].tracknr = tracknr++;
182  }
183 
184  item = trimmed_filename != nullptr ? timingtrim->GetItem(trimmed_filename, false) : nullptr;
185  if (item != nullptr && item->value.has_value() && !item->value->empty()) {
186  auto endpos = item->value->find(':');
187  if (endpos != std::string::npos) {
188  this->songinfo[i].override_start = atoi(item->value->c_str());
189  this->songinfo[i].override_end = atoi(item->value->c_str() + endpos + 1);
190  }
191  }
192  }
193  }
194  return ret;
195 }
base_media_func.h
BaseMedia::DetermineBestSet
static bool DetermineBestSet()
Determine the graphics pack that has to be used.
Definition: gfxinit.cpp:440
MusicSongInfo::tracknr
byte tracknr
track number of song displayed in UI
Definition: base_media_base.h:293
BASESET_DIR
@ BASESET_DIR
Subdirectory for all base data (base sets, intro game)
Definition: fileio_type.h:116
IniItem
A single "line" in an ini file.
Definition: ini_type.h:25
NUM_SONGS_AVAILABLE
static const uint NUM_SONGS_AVAILABLE
Maximum number of songs in the full playlist; theme song + the classes.
Definition: base_media_base.h:276
MusicSongInfo::override_start
int override_start
MIDI ticks to skip over in beginning.
Definition: base_media_base.h:298
IniGroup
A group within an ini file.
Definition: ini_type.h:38
_music_file_names
static const char *const _music_file_names[]
Names corresponding to the music set's files.
Definition: music.cpp:79
GetMusicCatEntryName
char * GetMusicCatEntryName(const char *filename, size_t entrynum)
Read the name of a music CAT file entry.
Definition: music.cpp:28
MD5File::check_result
ChecksumResult check_result
cached result of md5 check
Definition: base_media_base.h:37
GetMusicCatEntryData
byte * GetMusicCatEntryData(const char *filename, size_t entrynum, size_t &entrylen)
Read the full data of a music CAT file entry.
Definition: music.cpp:55
RandomAccessFile::ReadBlock
void ReadBlock(void *ptr, size_t size)
Read a block.
Definition: random_access_file.cpp:138
MusicSongInfo::override_end
int override_end
MIDI tick to end the song at (0 if no override)
Definition: base_media_base.h:299
RandomAccessFile::ReadByte
byte ReadByte()
Read a byte from the file.
Definition: random_access_file.cpp:100
random_access_file_type.h
RandomAccessFile::SkipBytes
void SkipBytes(int n)
Skip n bytes ahead in the file.
Definition: random_access_file.cpp:148
IniItem::value
std::optional< std::string > value
The value of this item.
Definition: ini_type.h:28
MusicSongInfo::songname
char songname[32]
name of song displayed in UI
Definition: base_media_base.h:292
BaseMedia< MusicSet >
MTT_STANDARDMIDI
@ MTT_STANDARDMIDI
Standard MIDI file.
Definition: base_media_base.h:286
safeguards.h
MTT_MPSMIDI
@ MTT_MPSMIDI
MPS GM driver MIDI format (contained in a CAT file)
Definition: base_media_base.h:287
StrEmpty
static bool StrEmpty(const char *s)
Check if a string buffer is empty.
Definition: string_func.h:64
MusicSet
All data of a music set.
Definition: base_media_base.h:303
MusicSet::songinfo
MusicSongInfo songinfo[NUM_SONGS_AVAILABLE]
Data about individual songs in set.
Definition: base_media_base.h:305
stdafx.h
RandomAccessFile::ReadDword
uint32 ReadDword()
Read a double word (32 bits) from the file (in low endian format).
Definition: random_access_file.cpp:127
INSTANTIATE_BASE_MEDIA_METHODS
#define INSTANTIATE_BASE_MEDIA_METHODS(repl_type, set_type)
Force instantiation of methods so we don't get linker errors.
Definition: base_media_func.h:377
FioCheckFileExists
bool FioCheckFileExists(const std::string &filename, Subdirectory subdir)
Check whether the given file exists.
Definition: fileio.cpp:108
IniFile
Ini file that supports both loading and saving.
Definition: ini_type.h:89
BaseSet
Information about a single base set.
Definition: base_media_base.h:49
MD5File::CR_NO_FILE
@ CR_NO_FILE
The file did not exist.
Definition: base_media_base.h:31
MD5File::filename
const char * filename
filename
Definition: base_media_base.h:34
RandomAccessFile::SeekTo
void SeekTo(size_t pos, int mode)
Seek in the current file.
Definition: random_access_file.cpp:83
MusicSongInfo::filename
const char * filename
file on disk containing song (when used in MusicSet class, this pointer is owned by MD5File object fo...
Definition: base_media_base.h:294
Debug
#define Debug(name, level, format_string,...)
Ouptut a line of debugging information.
Definition: debug.h:37
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:378
MusicSet::num_available
byte num_available
Number of valid songs in set.
Definition: base_media_base.h:307
IniGroup::GetItem
IniItem * GetItem(const std::string &name, bool create)
Get the item with the given name, and if it doesn't exist and create is true it creates a new item.
Definition: ini_load.cpp:91
strecpy
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: string.cpp:112
free
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: stdafx.h:460
BaseMedia::GetExtension
static const char * GetExtension()
Get the extension that is used to identify this set.
Definition: gfxinit.cpp:464
BaseSet::FillSetDetails
bool FillSetDetails(IniFile *ini, const char *path, const char *full_filename, bool allow_empty_filename=true)
Read the set information from a loaded ini.
Definition: base_media_func.h:39
lastof
#define lastof(x)
Get the last element of an fixed size array.
Definition: stdafx.h:394
MusicSongInfo::cat_index
int cat_index
entry index in CAT file, for filetype==MTT_MPSMIDI
Definition: base_media_base.h:296
BaseSet< MusicSet, NUM_SONGS_AVAILABLE, false >::files
MD5File files[NUM_FILES]
All files part of this set.
Definition: base_media_base.h:67
MusicSongInfo::filetype
MusicTrackType filetype
decoder required for song file
Definition: base_media_base.h:295
RandomAccessFile
A file from which bytes, words and double words are read in (potentially) a random order.
Definition: random_access_file_type.h:23
IniLoadFile::GetGroup
IniGroup * GetGroup(const std::string &name, bool create_new=true)
Get the group with the given name.
Definition: ini_load.cpp:163