summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/base_media_base.h40
-rw-r--r--src/base_media_func.h20
-rw-r--r--src/console_cmds.cpp2
-rw-r--r--src/gfxinit.cpp15
-rw-r--r--src/lang/english.txt8
-rw-r--r--src/misc.cpp2
-rw-r--r--src/music.cpp106
-rw-r--r--src/music.h25
-rw-r--r--src/music_gui.cpp150
-rw-r--r--src/network/core/tcp_content.h1
-rw-r--r--src/openttd.cpp17
-rw-r--r--src/settings_gui.cpp80
-rw-r--r--src/sound.cpp4
-rw-r--r--src/table/settings.h1
14 files changed, 346 insertions, 125 deletions
diff --git a/src/base_media_base.h b/src/base_media_base.h
index 1d52145b9..7567d1b81 100644
--- a/src/base_media_base.h
+++ b/src/base_media_base.h
@@ -14,6 +14,7 @@
#include "fileio_func.h"
#include "core/smallmap_type.hpp"
+#include "gfx_type.h"
/* Forward declare these; can't do 'struct X' in functions as older GCCs barf on that */
struct IniFile;
@@ -32,21 +33,25 @@ struct MD5File {
uint8 hash[16]; ///< md5 sum of the file
const char *missing_warning; ///< warning when this file is missing
- ChecksumResult CheckMD5() const;
+ ChecksumResult CheckMD5(Subdirectory subdir) const;
};
/**
* Information about a single base set.
* @tparam T the real class we're going to be
* @tparam Tnum_files the number of files in the set
+ * @tparam Tsubdir the subdirectory where to find the files
*/
-template <class T, size_t Tnum_files>
+template <class T, size_t Tnum_files, Subdirectory Tsubdir>
struct BaseSet {
typedef SmallMap<const char *, const char *> TranslatedStrings;
/** Number of files in this set */
static const size_t NUM_FILES = Tnum_files;
+ /** The sub directory to search for the files */
+ static const Subdirectory SUBDIR = Tsubdir;
+
/** Internal names of the files in this set. */
static const char * const *file_names;
@@ -163,7 +168,7 @@ public:
static uint FindSets()
{
BaseMedia<Tbase_set> fs;
- return fs.Scan(GetExtension(), DATA_DIR);
+ return fs.Scan(GetExtension(), Tbase_set::SUBDIR);
}
/**
@@ -226,7 +231,7 @@ enum GraphicsFileType {
};
/** All data of a graphics set. */
-struct GraphicsSet : BaseSet<GraphicsSet, MAX_GFT> {
+struct GraphicsSet : BaseSet<GraphicsSet, MAX_GFT, DATA_DIR> {
PaletteType palette; ///< Palette of this graphics set
bool FillSetDetails(struct IniFile *ini, const char *path);
@@ -242,7 +247,7 @@ public:
};
/** All data of a sounds set. */
-struct SoundsSet : BaseSet<SoundsSet, 1> {
+struct SoundsSet : BaseSet<SoundsSet, 1, DATA_DIR> {
};
/** All data/functions related with replacing the base sounds */
@@ -250,4 +255,29 @@ class BaseSounds : public BaseMedia<SoundsSet> {
public:
};
+/** Maximum number of songs in the 'class' playlists. */
+static const uint NUM_SONGS_CLASS = 10;
+/** Number of classes for songs */
+static const uint NUM_SONG_CLASSES = 3;
+/** Maximum number of songs in the full playlist; theme song + the classes */
+static const uint NUM_SONGS_AVAILABLE = 1 + NUM_SONG_CLASSES * NUM_SONGS_CLASS;
+
+/** Maximum number of songs in the (custom) playlist */
+static const uint NUM_SONGS_PLAYLIST = 32;
+
+/** All data of a music set. */
+struct MusicSet : BaseSet<MusicSet, NUM_SONGS_AVAILABLE, GM_DIR> {
+ /** The name of the different songs. */
+ char song_name[NUM_SONGS_AVAILABLE][32];
+ byte track_nr[NUM_SONGS_AVAILABLE];
+ byte num_available;
+
+ bool FillSetDetails(struct IniFile *ini, const char *path);
+};
+
+/** All data/functions related with replacing the base music */
+class BaseMusic : public BaseMedia<MusicSet> {
+public:
+};
+
#endif /* BASE_MEDIA_BASE_H */
diff --git a/src/base_media_func.h b/src/base_media_func.h
index df49f0b1b..6db7137e5 100644
--- a/src/base_media_func.h
+++ b/src/base_media_func.h
@@ -28,8 +28,8 @@ template <class Tbase_set> /* static */ Tbase_set *BaseMedia<Tbase_set>::availab
return false; \
}
-template <class T, size_t Tnum_files>
-bool BaseSet<T, Tnum_files>::FillSetDetails(IniFile *ini, const char *path)
+template <class T, size_t Tnum_files, Subdirectory Tsubdir>
+bool BaseSet<T, Tnum_files, Tsubdir>::FillSetDetails(IniFile *ini, const char *path)
{
memset(this, 0, sizeof(*this));
@@ -64,13 +64,21 @@ bool BaseSet<T, Tnum_files>::FillSetDetails(IniFile *ini, const char *path)
for (uint i = 0; i < Tnum_files; i++) {
MD5File *file = &this->files[i];
/* Find the filename first. */
- item = files->GetItem(BaseSet<T, Tnum_files>::file_names[i], false);
+ item = files->GetItem(BaseSet<T, Tnum_files, Tsubdir>::file_names[i], false);
if (item == NULL) {
- DEBUG(grf, 0, "No " SET_TYPE " file for: %s", BaseSet<T, Tnum_files>::file_names[i]);
+ DEBUG(grf, 0, "No " SET_TYPE " file for: %s", BaseSet<T, Tnum_files, Tsubdir>::file_names[i]);
return false;
}
const char *filename = item->value;
+ if (filename == NULL) {
+ file->filename = NULL;
+ /* If we list no file, that file must be valid */
+ this->valid_files++;
+ this->found_files++;
+ continue;
+ }
+
file->filename = str_fmt("%s%s", path, filename);
/* Then find the MD5 checksum */
@@ -100,7 +108,7 @@ bool BaseSet<T, Tnum_files>::FillSetDetails(IniFile *ini, const char *path)
}
/* Then find the warning message when the file's missing */
- item = origin->GetItem(filename, false);
+ item = filename == NULL ? NULL : origin->GetItem(filename, false);
if (item == NULL) item = origin->GetItem("default", false);
if (item == NULL) {
DEBUG(grf, 1, "No origin warning message specified for: %s", filename);
@@ -109,7 +117,7 @@ bool BaseSet<T, Tnum_files>::FillSetDetails(IniFile *ini, const char *path)
file->missing_warning = strdup(item->value);
}
- switch (file->CheckMD5()) {
+ switch (file->CheckMD5(Tsubdir)) {
case MD5File::CR_MATCH:
this->valid_files++;
/* FALL THROUGH */
diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp
index 70b251ac6..2d1261482 100644
--- a/src/console_cmds.cpp
+++ b/src/console_cmds.cpp
@@ -1669,7 +1669,7 @@ DEF_CONSOLE_CMD(ConContent)
if (strcasecmp(argv[1], "state") == 0) {
IConsolePrintF(CC_WHITE, "id, type, state, name");
for (ConstContentIterator iter = _network_content_client.Begin(); iter != _network_content_client.End(); iter++) {
- static const char * const types[] = { "Base graphics", "NewGRF", "AI", "AI library", "Scenario", "Heightmap", "Base sound" };
+ static const char * const types[] = { "Base graphics", "NewGRF", "AI", "AI library", "Scenario", "Heightmap", "Base sound", "Base music" };
assert_compile(lengthof(types) == CONTENT_TYPE_END - CONTENT_TYPE_BEGIN);
static const char * const states[] = { "Not selected", "Selected", "Dep Selected", "Installed", "Unknown" };
static const ConsoleColour state_to_colour[] = { CC_COMMAND, CC_INFO, CC_INFO, CC_WHITE, CC_ERROR };
diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp
index c686349b2..ae01f6571 100644
--- a/src/gfxinit.cpp
+++ b/src/gfxinit.cpp
@@ -118,7 +118,7 @@ void CheckExternalFiles()
/* Not all files were loaded succesfully, see which ones */
add_pos += seprintf(add_pos, last, "Trying to load graphics set '%s', but it is incomplete. The game will probably not run correctly until you properly install this set or select another one. See section 4.1 of readme.txt.\n\nThe following files are corrupted or missing:\n", used_set->name);
for (uint i = 0; i < GraphicsSet::NUM_FILES; i++) {
- MD5File::ChecksumResult res = used_set->files[i].CheckMD5();
+ MD5File::ChecksumResult res = used_set->files[i].CheckMD5(DATA_DIR);
if (res != MD5File::CR_MATCH) add_pos += seprintf(add_pos, last, "\t%s is %s (%s)\n", used_set->files[i].filename, res == MD5File::CR_MISMATCH ? "corrupt" : "missing", used_set->files[i].missing_warning);
}
add_pos += seprintf(add_pos, last, "\n");
@@ -131,7 +131,7 @@ void CheckExternalFiles()
assert_compile(SoundsSet::NUM_FILES == 1);
/* No need to loop each file, as long as there is only a single
* sound file. */
- add_pos += seprintf(add_pos, last, "\t%s is %s (%s)\n", sounds_set->files->filename, sounds_set->files->CheckMD5() == MD5File::CR_MISMATCH ? "corrupt" : "missing", sounds_set->files->missing_warning);
+ add_pos += seprintf(add_pos, last, "\t%s is %s (%s)\n", sounds_set->files->filename, sounds_set->files->CheckMD5(DATA_DIR) == MD5File::CR_MISMATCH ? "corrupt" : "missing", sounds_set->files->missing_warning);
}
if (add_pos != error_msg) ShowInfoF("%s", error_msg);
@@ -206,7 +206,7 @@ void GfxLoadSprites()
bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path)
{
- bool ret = this->BaseSet<GraphicsSet, MAX_GFT>::FillSetDetails(ini, path);
+ bool ret = this->BaseSet<GraphicsSet, MAX_GFT, DATA_DIR>::FillSetDetails(ini, path);
if (ret) {
IniGroup *metadata = ini->GetGroup("metadata");
IniItem *item;
@@ -220,15 +220,16 @@ bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path)
/**
* Calculate and check the MD5 hash of the supplied filename.
+ * @param subdir The sub directory to get the files from
* @return
* CR_MATCH if the MD5 hash matches
* CR_MISMATCH if the MD5 does not match
* CR_NO_FILE if the file misses
*/
-MD5File::ChecksumResult MD5File::CheckMD5() const
+MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir) const
{
size_t size;
- FILE *f = FioFOpenFile(this->filename, "rb", DATA_DIR, &size);
+ FILE *f = FioFOpenFile(this->filename, "rb", subdir, &size);
if (f == NULL) return CR_NO_FILE;
@@ -252,8 +253,8 @@ MD5File::ChecksumResult MD5File::CheckMD5() const
static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
/** Implementation */
-template <class T, size_t Tnum_files>
-/* static */ const char * const *BaseSet<T, Tnum_files>::file_names = _graphics_file_names;
+template <class T, size_t Tnum_files, Subdirectory Tsubdir>
+/* static */ const char * const *BaseSet<T, Tnum_files, Tsubdir>::file_names = _graphics_file_names;
extern void UpdateNewGRFConfigPalette();
diff --git a/src/lang/english.txt b/src/lang/english.txt
index c3c99f358..c8e7d350c 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -607,6 +607,8 @@ STR_MUSIC_TOOLTIP_SELECT_CUSTOM_2_USER_DEFINED :{BLACK}Select '
STR_MUSIC_TOOLTIP_TOGGLE_PROGRAM_SHUFFLE :{BLACK}Toggle programme shuffle on/off
STR_MUSIC_TOOLTIP_SHOW_MUSIC_TRACK_SELECTION :{BLACK}Show music track selection window
+STR_ERROR_NO_SONGS :{WHITE}A music set without songs has been selected. No songs will be played
+
# Playlist window
STR_PLAYLIST_MUSIC_PROGRAM_SELECTION :{WHITE}Music Programme Selection
STR_PLAYLIST_TRACK_NAME :{TINYFONT}{LTBLUE}{ZEROFILL_NUM} "{RAW_STRING}"
@@ -942,6 +944,11 @@ STR_GAME_OPTIONS_BASE_SFX :{BLACK}Base sou
STR_GAME_OPTIONS_BASE_SFX_TOOLTIP :{BLACK}Select the base sounds set to use
STR_GAME_OPTIONS_BASE_SFX_DESCRIPTION_TOOLTIP :{BLACK}Additional information about the base sounds set
+STR_GAME_OPTIONS_BASE_MUSIC :{BLACK}Base music set
+STR_GAME_OPTIONS_BASE_MUSIC_TOOLTIP :{BLACK}Select the base music set to use
+STR_GAME_OPTIONS_BASE_MUSIC_STATUS :{RED}{NUM} corrupted file{P "" s}
+STR_GAME_OPTIONS_BASE_MUSIC_DESCRIPTION_TOOLTIP :{BLACK}Additional information about the base music set
+
STR_ERROR_FULLSCREEN_FAILED :{WHITE}Fullscreen mode failed
# Custom currency window
@@ -1812,6 +1819,7 @@ STR_CONTENT_TYPE_AI_LIBRARY :AI library
STR_CONTENT_TYPE_SCENARIO :Scenario
STR_CONTENT_TYPE_HEIGHTMAP :Heightmap
STR_CONTENT_TYPE_BASE_SOUNDS :Base sounds
+STR_CONTENT_TYPE_BASE_MUSIC :Base music
# Content downloading progress window
STR_CONTENT_DOWNLOAD_TITLE :{WHITE}Downloading content...
diff --git a/src/misc.cpp b/src/misc.cpp
index 1a163e6c8..995f0dd5e 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -31,6 +31,7 @@ extern TileIndex _cur_tileloop_tile;
extern void MakeNewgameSettingsLive();
void InitializeSound();
+void InitializeMusic();
void InitializeVehicles();
void InitializeDepots();
void InitializeEngineRenews();
@@ -71,6 +72,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
if (reset_settings) MakeNewgameSettingsLive();
InitializeSound();
+ InitializeMusic();
if (reset_date) {
SetDate(ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1));
diff --git a/src/music.cpp b/src/music.cpp
index 87c26dad4..813bc5a16 100644
--- a/src/music.cpp
+++ b/src/music.cpp
@@ -10,31 +10,85 @@
/** @file music.cpp The songs that OpenTTD knows. */
#include "stdafx.h"
-#include "music.h"
-
-const SongSpecs _origin_songs_specs[] = {
- {"gm_tt00.gm", "Tycoon DELUXE Theme"},
- {"gm_tt02.gm", "Easy Driver"},
- {"gm_tt03.gm", "Little Red Diesel"},
- {"gm_tt17.gm", "Cruise Control"},
- {"gm_tt07.gm", "Don't Walk!"},
- {"gm_tt09.gm", "Fell Apart On Me"},
- {"gm_tt04.gm", "City Groove"},
- {"gm_tt19.gm", "Funk Central"},
- {"gm_tt06.gm", "Stoke It"},
- {"gm_tt12.gm", "Road Hog"},
- {"gm_tt05.gm", "Aliens Ate My Railway"},
- {"gm_tt01.gm", "Snarl Up"},
- {"gm_tt18.gm", "Stroll On"},
- {"gm_tt10.gm", "Can't Get There From Here"},
- {"gm_tt08.gm", "Sawyer's Tune"},
- {"gm_tt13.gm", "Hold That Train!"},
- {"gm_tt21.gm", "Movin' On"},
- {"gm_tt15.gm", "Goss Groove"},
- {"gm_tt16.gm", "Small Town"},
- {"gm_tt14.gm", "Broomer's Oil Rag"},
- {"gm_tt20.gm", "Jammit"},
- {"gm_tt11.gm", "Hard Drivin'"},
+#include "debug.h"
+
+/* The type of set we're replacing */
+#define SET_TYPE "music"
+#include "base_media_func.h"
+
+INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<MusicSet>, MusicSet)
+
+/** Names corresponding to the music set's files */
+static const char * const _music_file_names[] = {
+ "theme",
+ "old_0", "old_1", "old_2", "old_3", "old_4", "old_5", "old_6", "old_7", "old_8", "old_9",
+ "new_0", "new_1", "new_2", "new_3", "new_4", "new_5", "new_6", "new_7", "new_8", "new_9",
+ "ezy_0", "ezy_1", "ezy_2", "ezy_3", "ezy_4", "ezy_5", "ezy_6", "ezy_7", "ezy_8", "ezy_9",
};
+assert_compile(lengthof(_music_file_names) == NUM_SONGS_AVAILABLE);
+
+template <class T, size_t Tnum_files, Subdirectory Tsubdir>
+/* static */ const char * const *BaseSet<T, Tnum_files, Tsubdir>::file_names = _music_file_names;
+
+template <class Tbase_set>
+/* static */ const char *BaseMedia<Tbase_set>::GetExtension()
+{
+ return ".obm"; // OpenTTD Base Music
+}
+
+template <class Tbase_set>
+/* static */ bool BaseMedia<Tbase_set>::DetermineBestSet()
+{
+ if (BaseMedia<Tbase_set>::used_set != NULL) return true;
+
+ const Tbase_set *best = NULL;
+ for (const Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != NULL; c = c->next) {
+ if (best == NULL ||
+ best->valid_files < c->valid_files ||
+ (best->valid_files == c->valid_files &&
+ (best->shortname == c->shortname && best->version < c->version))) {
+ best = c;
+ }
+ }
+
+ BaseMedia<Tbase_set>::used_set = best;
+ return BaseMedia<Tbase_set>::used_set != NULL;
+}
+
+/**
+ * Try to read a single piece of metadata and return false if it doesn't exist.
+ * @param name the name of the item to fetch.
+ */
+#define fetch_name(name) \
+ item = metadata->GetItem(name, false); \
+ if (item == NULL || strlen(item->value) == 0) { \
+ DEBUG(grf, 0, "Base " SET_TYPE "set detail loading: %s field missing", name); \
+ return false; \
+ }
+
+bool MusicSet::FillSetDetails(IniFile *ini, const char *path)
+{
+ bool ret = this->BaseSet<MusicSet, NUM_SONGS_AVAILABLE, GM_DIR>::FillSetDetails(ini, path);
+ if (ret) {
+ this->num_available = 0;
+ IniGroup *names = ini->GetGroup("names");
+ for (uint i = 0, j = 1; i < lengthof(this->song_name); i++) {
+ const char *filename = this->files[i].filename;
+ if (names == NULL || StrEmpty(filename)) {
+ this->song_name[i][0] = '\0';
+ continue;
+ }
+
+ IniItem *item = names->GetItem(filename, false);
+ if (item == NULL || strlen(item->value) == 0) {
+ DEBUG(grf, 0, "Base music set song name missing: %s", filename);
+ return false;
+ }
-assert_compile(NUM_SONGS_AVAILABLE == lengthof(_origin_songs_specs));
+ strecpy(this->song_name[i], item->value, lastof(this->song_name[i]));
+ this->track_nr[i] = j++;
+ this->num_available++;
+ }
+ }
+ return true;
+}
diff --git a/src/music.h b/src/music.h
deleted file mode 100644
index 32b30ed26..000000000
--- a/src/music.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* $Id$ */
-
-/*
- * This file is part of OpenTTD.
- * 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.
- * 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.
- * 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/>.
- */
-
-/** @file music.h Base for the music handling. */
-
-#ifndef MUSIC_H
-#define MUSIC_H
-
-#define NUM_SONGS_PLAYLIST 33
-#define NUM_SONGS_AVAILABLE 22
-
-struct SongSpecs {
- char filename[MAX_PATH];
- char song_name[64];
-};
-
-extern const SongSpecs _origin_songs_specs[];
-
-#endif /* MUSIC_H */
diff --git a/src/music_gui.cpp b/src/music_gui.cpp
index eedb5c425..00ce891f3 100644
--- a/src/music_gui.cpp
+++ b/src/music_gui.cpp
@@ -12,7 +12,7 @@
#include "stdafx.h"
#include "openttd.h"
#include "fileio_func.h"
-#include "music.h"
+#include "base_media_base.h"
#include "music/music_driver.hpp"
#include "window_gui.h"
#include "strings_func.h"
@@ -20,6 +20,7 @@
#include "sound_func.h"
#include "gfx_func.h"
#include "core/random_func.hpp"
+#include "gui.h"
#include "table/strings.h"
#include "table/sprites.h"
@@ -31,32 +32,40 @@
*/
static const char *GetSongName(int index)
{
- return _origin_songs_specs[index].song_name;
+ return BaseMusic::GetUsedSet()->song_name[index];
}
+/**
+ * Get the track number of the song.
+ * @param index of the song.
+ * @return the track number of the song.
+ */
+static int GetTrackNumber(int index)
+{
+ return BaseMusic::GetUsedSet()->track_nr[index];
+}
-static byte _music_wnd_cursong;
-static bool _song_is_active;
-static byte _cur_playlist[NUM_SONGS_PLAYLIST];
-
+/** The currently played song */
+static byte _music_wnd_cursong = 1;
+/** Whether a song is currently played */
+static bool _song_is_active = false;
+/** Indices of the songs in the current playlist */
+static byte _cur_playlist[NUM_SONGS_PLAYLIST + 1];
-static byte _playlist_all[] = {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0
-};
+/** Indices of all songs */
+static byte _playlist_all[NUM_SONGS_AVAILABLE + 1];
+/** Indices of all old style songs */
+static byte _playlist_old_style[NUM_SONGS_CLASS + 1];
+/** Indices of all new style songs */
+static byte _playlist_new_style[NUM_SONGS_CLASS + 1];
+/** Indices of all ezy street songs */
+static byte _playlist_ezy_street[NUM_SONGS_CLASS + 1];
-static byte _playlist_old_style[] = {
- 2, 9, 3, 10, 15, 16, 20, 14, 0
-};
-
-static byte _playlist_new_style[] = {
- 7, 12, 11, 18, 22, 19, 6, 0
-};
-
-static byte _playlist_ezy_street[] = {
- 13, 8, 17, 4, 21, 5, 0
-};
+assert_compile(lengthof(msf.custom_1) == NUM_SONGS_PLAYLIST + 1);
+assert_compile(lengthof(msf.custom_2) == NUM_SONGS_PLAYLIST + 1);
+/** The different playlists that can be played. */
static byte * const _playlists[] = {
_playlist_all,
_playlist_old_style,
@@ -66,6 +75,57 @@ static byte * const _playlists[] = {
msf.custom_2,
};
+/**
+ * Validate a playlist.
+ * @param playlist the playlist to validate
+ */
+void ValidatePlaylist(byte *playlist)
+{
+ while (*playlist != 0) {
+ if (*playlist <= BaseMusic::GetUsedSet()->num_available) {
+ playlist++;
+ continue;
+ }
+ for (byte *p = playlist; *p != 0; p++) {
+ p[0] = p[1];
+ }
+ }
+}
+
+/** Initialize the playlists */
+void InitializeMusic()
+{
+ uint j = 0;
+ for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
+ if (StrEmpty(GetSongName(i))) continue;
+ _playlist_all[j++] = i + 1;
+ }
+ /* Terminate the list */
+ _playlist_all[j] = 0;
+
+ /* Now make the 'styled' playlists */
+ for (uint k = 0; k < NUM_SONG_CLASSES; k++) {
+ j = 0;
+ for (uint i = 0; i < NUM_SONGS_CLASS; i++) {
+ int id = k * NUM_SONGS_CLASS + i + 1;
+ if (StrEmpty(GetSongName(id))) continue;
+ _playlists[k + 1][j++] = id + 1;
+ }
+ /* Terminate the list */
+ _playlists[k + 1][j] = 0;
+ }
+
+ ValidatePlaylist(msf.custom_1);
+ ValidatePlaylist(msf.custom_2);
+
+ if (BaseMusic::GetUsedSet()->num_available < _music_wnd_cursong) {
+ /* If there are less songs than the currently played song,
+ * just pause and reset to no song. */
+ _music_wnd_cursong = 0;
+ _song_is_active = false;
+ }
+}
+
static void SkipToPrevSong()
{
byte *b = _cur_playlist;
@@ -112,13 +172,15 @@ static void DoPlaySong()
{
char filename[MAX_PATH];
FioFindFullPath(filename, lengthof(filename), GM_DIR,
- _origin_songs_specs[_music_wnd_cursong - 1].filename);
+ BaseMusic::GetUsedSet()->files[_music_wnd_cursong - 1].filename);
_music_driver->PlaySong(filename);
+ SetWindowDirty(WC_MUSIC_WINDOW, 0);
}
static void DoStopMusic()
{
_music_driver->StopSong();
+ SetWindowDirty(WC_MUSIC_WINDOW, 0);
}
static void SelectSongToPlay()
@@ -128,9 +190,10 @@ static void SelectSongToPlay()
memset(_cur_playlist, 0, sizeof(_cur_playlist));
do {
+ const char *filename = BaseMusic::GetUsedSet()->files[_playlists[msf.playlist][i] - 1].filename;
/* We are now checking for the existence of that file prior
* to add it to the list of available songs */
- if (FioCheckFileExists(_origin_songs_specs[_playlists[msf.playlist][i] - 1].filename, GM_DIR)) {
+ if (!StrEmpty(filename) && FioCheckFileExists(filename, GM_DIR)) {
_cur_playlist[j] = _playlists[msf.playlist][i];
j++;
}
@@ -275,10 +338,13 @@ struct MusicTrackSelectionWindow : public Window {
case MTSW_LIST_LEFT: case MTSW_LIST_RIGHT: {
Dimension d = {0, 0};
- for (uint i = 1; i <= NUM_SONGS_AVAILABLE; i++) {
- SetDParam(0, i);
+ for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
+ const char *song_name = GetSongName(i);
+ if (StrEmpty(song_name)) continue;
+
+ SetDParam(0, GetTrackNumber(i));
SetDParam(1, 2);
- SetDParamStr(2, GetSongName(i - 1));
+ SetDParamStr(2, GetSongName(i));
Dimension d2 = GetStringBoundingBox(STR_PLAYLIST_TRACK_NAME);
d.width = max(d.width, d2.width);
d.height += d2.height;
@@ -297,10 +363,13 @@ struct MusicTrackSelectionWindow : public Window {
GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, 0);
int y = r.top + WD_FRAMERECT_TOP;
- for (uint i = 1; i <= NUM_SONGS_AVAILABLE; i++) {
- SetDParam(0, i);
+ for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
+ const char *song_name = GetSongName(i);
+ if (StrEmpty(song_name)) continue;
+
+ SetDParam(0, GetTrackNumber(i));
SetDParam(1, 2);
- SetDParamStr(2, GetSongName(i - 1));
+ SetDParamStr(2, song_name);
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
y += FONT_HEIGHT_SMALL;
}
@@ -311,10 +380,10 @@ struct MusicTrackSelectionWindow : public Window {
int y = r.top + WD_FRAMERECT_TOP;
for (const byte *p = _playlists[msf.playlist]; *p != 0; p++) {
- uint i = *p;
- SetDParam(0, i);
+ uint i = *p - 1;
+ SetDParam(0, GetTrackNumber(i));
SetDParam(1, 2);
- SetDParamStr(2, GetSongName(i - 1));
+ SetDParamStr(2, GetSongName(i));
DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
y += FONT_HEIGHT_SMALL;
}
@@ -334,12 +403,18 @@ struct MusicTrackSelectionWindow : public Window {
int y = (pt.y - this->GetWidget<NWidgetBase>(widget)->pos_y) / FONT_HEIGHT_SMALL;
if (msf.playlist < 4) return;
- if (!IsInsideMM(y, 0, NUM_SONGS_AVAILABLE)) return;
+ if (!IsInsideMM(y, 0, BaseMusic::GetUsedSet()->num_available)) return;
byte *p = _playlists[msf.playlist];
for (uint i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) {
if (p[i] == 0) {
- p[i] = y + 1;
+ /* Find the actual song number */
+ for (uint j = 0; j < NUM_SONGS_AVAILABLE; j++) {
+ if (GetTrackNumber(j) == y + 1) {
+ p[i] = j + 1;
+ break;
+ }
+ }
p[i + 1] = 0;
this->SetDirty();
SelectSongToPlay();
@@ -352,7 +427,7 @@ struct MusicTrackSelectionWindow : public Window {
int y = (pt.y - this->GetWidget<NWidgetBase>(widget)->pos_y) / FONT_HEIGHT_SMALL;
if (msf.playlist < 4) return;
- if (!IsInsideMM(y, 0, NUM_SONGS_AVAILABLE)) return;
+ if (!IsInsideMM(y, 0, NUM_SONGS_PLAYLIST)) return;
byte *p = _playlists[msf.playlist];
for (uint i = y; i != NUM_SONGS_PLAYLIST - 1; i++) {
@@ -364,7 +439,7 @@ struct MusicTrackSelectionWindow : public Window {
} break;
case MTSW_CLEAR: // clear
- _playlists[msf.playlist][0] = 0;
+ for (uint i = 0; _playlists[msf.playlist][i] != 0; i++) _playlists[msf.playlist][i] = 0;
this->SetDirty();
StopMusic();
SelectSongToPlay();
@@ -484,7 +559,7 @@ struct MusicWindow : public Window {
case MW_TRACK_NAME: {
Dimension d = GetStringBoundingBox(STR_MUSIC_TITLE_NONE);
- for (int i = 0; i < NUM_SONGS_AVAILABLE; i++) {
+ for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
SetDParamStr(0, GetSongName(i));
d = maxdim(d, GetStringBoundingBox(STR_MUSIC_TITLE_NAME));
}
@@ -523,7 +598,7 @@ struct MusicWindow : public Window {
GfxFillRect(r.left + 1, r.top + 1, r.right, r.bottom, 0);
StringID str = STR_MUSIC_TRACK_NONE;
if (_song_is_active != 0 && _music_wnd_cursong != 0) {
- SetDParam(0, _music_wnd_cursong);
+ SetDParam(0, GetTrackNumber(_music_wnd_cursong - 1));
SetDParam(1, 2);
str = STR_MUSIC_TRACK_DIGIT;
}
@@ -727,5 +802,6 @@ static const WindowDesc _music_window_desc(
void ShowMusicWindow()
{
+ if (BaseMusic::GetUsedSet()->num_available == 0) ShowErrorMessage(STR_ERROR_NO_SONGS, INVALID_STRING_ID, 0, 0);
AllocateWindowDescFront<MusicWindow>(&_music_window_desc, 0);
}
diff --git a/src/network/core/tcp_content.h b/src/network/core/tcp_content.h
index 2c54641eb..3b82696df 100644
--- a/src/network/core/tcp_content.h
+++ b/src/network/core/tcp_content.h
@@ -31,6 +31,7 @@ enum ContentType {
CONTENT_TYPE_SCENARIO = 5, ///< The content consists of a scenario
CONTENT_TYPE_HEIGHTMAP = 6, ///< The content consists of a heightmap
CONTENT_TYPE_BASE_SOUNDS = 7, ///< The content consists of base sounds
+ CONTENT_TYPE_BASE_MUSIC = 8, ///< The content consists of base music
CONTENT_TYPE_END, ///< Helper to mark the end of the types
};
diff --git a/src/openttd.cpp b/src/openttd.cpp
index 300f14462..e790e3724 100644
--- a/src/openttd.cpp
+++ b/src/openttd.cpp
@@ -181,6 +181,7 @@ static void ShowHelp()
" specified in graphics set file (see below)\n"
" -I graphics_set = Force the graphics set (see below)\n"
" -S sounds_set = Force the sounds set (see below)\n"
+ " -M music_set = Force the music set (see below)\n"
" -c config_file = Use 'config_file' instead of 'openttd.cfg'\n"
" -x = Do not automatically save to config file on exit\n"
"\n",
@@ -193,6 +194,9 @@ static void ShowHelp()
/* List the sounds packs */
p = BaseSounds::GetSetsList(p, lastof(buf));
+ /* List the music packs */
+ p = BaseMusic::GetSetsList(p, lastof(buf));
+
/* List the drivers */
p = VideoDriverFactoryBase::GetDriversInfo(p, lastof(buf));
@@ -409,6 +413,7 @@ int ttd_main(int argc, char *argv[])
char *blitter = NULL;
char *graphics_set = NULL;
char *sounds_set = NULL;
+ char *music_set = NULL;
Dimension resolution = {0, 0};
Year startyear = INVALID_YEAR;
uint generation_seed = GENERATE_NEW_SEED;
@@ -446,6 +451,7 @@ int ttd_main(int argc, char *argv[])
switch (i) {
case 'I': free(graphics_set); graphics_set = strdup(mgo.opt); break;
case 'S': free(sounds_set); sounds_set = strdup(mgo.opt); break;
+ case 'M': free(music_set); music_set = strdup(mgo.opt); break;
case 'm': free(musicdriver); musicdriver = strdup(mgo.opt); break;
case 's': free(sounddriver); sounddriver = strdup(mgo.opt); break;
case 'v': free(videodriver); videodriver = strdup(mgo.opt); break;
@@ -536,6 +542,7 @@ int ttd_main(int argc, char *argv[])
DeterminePaths(argv[0]);
BaseGraphics::FindSets();
BaseSounds::FindSets();
+ BaseMusic::FindSets();
ShowHelp();
return 0;
}
@@ -549,6 +556,7 @@ int ttd_main(int argc, char *argv[])
DeterminePaths(argv[0]);
BaseGraphics::FindSets();
BaseSounds::FindSets();
+ BaseMusic::FindSets();
#if defined(UNIX) && !defined(__MORPHOS__)
/* We must fork here, or we'll end up without some resources we need (like sockets) */
@@ -620,6 +628,14 @@ int ttd_main(int argc, char *argv[])
}
free(graphics_set);
+ if (music_set == NULL && BaseMusic::ini_set != NULL) music_set = strdup(BaseMusic::ini_set);
+ if (!BaseMusic::SetSet(music_set)) {
+ StrEmpty(music_set) ?
+ usererror("Failed to find a music set. Please acquire a music set for OpenTTD. See section 4.1 of readme.txt.") :
+ usererror("Failed to select requested music set '%s'", music_set);
+ }
+ free(music_set);
+
/* Initialize game palette */
GfxInitPalettes();
@@ -748,6 +764,7 @@ int ttd_main(int argc, char *argv[])
free(const_cast<char *>(BaseGraphics::ini_set));
free(const_cast<char *>(BaseSounds::ini_set));
+ free(const_cast<char *>(BaseMusic::ini_set));
free(_ini_musicdriver);
free(_ini_sounddriver);
free(_ini_videodriver);
diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp
index 82c45a53d..8f6f8a0e6 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -104,21 +104,24 @@ static int GetCurRes()
/** Widgets of the game options menu */
enum GameOptionsWidgets {
- GOW_BACKGROUND, ///< Background of the window
- GOW_CURRENCY_DROPDOWN, ///< Currency dropdown
- GOW_DISTANCE_DROPDOWN, ///< Measuring unit dropdown
- GOW_ROADSIDE_DROPDOWN, ///< Dropdown to select the road side (to set the right side ;))
- GOW_TOWNNAME_DROPDOWN, ///< Town name dropdown
- GOW_AUTOSAVE_DROPDOWN, ///< Dropdown to say how often to autosave
- GOW_LANG_DROPDOWN, ///< Language dropdown
- GOW_RESOLUTION_DROPDOWN, ///< Dropdown for the resolution
- GOW_FULLSCREEN_BUTTON, ///< Toggle fullscreen
- GOW_SCREENSHOT_DROPDOWN, ///< Select the screenshot type... please use PNG!
- GOW_BASE_GRF_DROPDOWN, ///< Use to select a base GRF
- GOW_BASE_GRF_STATUS, ///< Info about missing files etc.
- GOW_BASE_GRF_DESCRIPTION,///< Description of selected base GRF
- GOW_BASE_SFX_DROPDOWN, ///< Use to select a base SFX
- GOW_BASE_SFX_DESCRIPTION,///< Description of selected base SFX
+ GOW_BACKGROUND, ///< Background of the window
+ GOW_CURRENCY_DROPDOWN, ///< Currency dropdown
+ GOW_DISTANCE_DROPDOWN, ///< Measuring unit dropdown
+ GOW_ROADSIDE_DROPDOWN, ///< Dropdown to select the road side (to set the right side ;))
+ GOW_TOWNNAME_DROPDOWN, ///< Town name dropdown
+ GOW_AUTOSAVE_DROPDOWN, ///< Dropdown to say how often to autosave
+ GOW_LANG_DROPDOWN, ///< Language dropdown
+ GOW_RESOLUTION_DROPDOWN, ///< Dropdown for the resolution
+ GOW_FULLSCREEN_BUTTON, ///< Toggle fullscreen
+ GOW_SCREENSHOT_DROPDOWN, ///< Select the screenshot type... please use PNG!
+ GOW_BASE_GRF_DROPDOWN, ///< Use to select a base GRF
+ GOW_BASE_GRF_STATUS, ///< Info about missing files etc.
+ GOW_BASE_GRF_DESCRIPTION, ///< Description of selected base GRF
+ GOW_BASE_SFX_DROPDOWN, ///< Use to select a base SFX
+ GOW_BASE_SFX_DESCRIPTION, ///< Description of selected base SFX
+ GOW_BASE_MUSIC_DROPDOWN, ///< Use to select a base music set
+ GOW_BASE_MUSIC_STATUS, ///< Info about corrupted files etc.
+ GOW_BASE_MUSIC_DESCRIPTION, ///< Description of selected base music set
};
/**
@@ -194,6 +197,8 @@ struct GameOptionsWindow : Window {
case GOW_BASE_GRF_DROPDOWN: SetDParamStr(0, BaseGraphics::GetUsedSet()->name); break;
case GOW_BASE_GRF_STATUS: SetDParam(0, BaseGraphics::GetUsedSet()->GetNumInvalid()); break;
case GOW_BASE_SFX_DROPDOWN: SetDParamStr(0, BaseSounds::GetUsedSet()->name); break;
+ case GOW_BASE_MUSIC_DROPDOWN: SetDParamStr(0, BaseMusic::GetUsedSet()->name); break;
+ case GOW_BASE_MUSIC_STATUS: SetDParam(0, BaseMusic::GetUsedSet()->GetNumInvalid()); break;
}
}
@@ -214,6 +219,11 @@ struct GameOptionsWindow : Window {
SetDParamStr(0, BaseSounds::GetUsedSet()->GetDescription(GetCurrentLanguageIsoCode()));
DrawStringMultiLine(r.left, r.right, r.top, UINT16_MAX, STR_BLACK_RAW_STRING);
break;
+
+ case GOW_BASE_MUSIC_DESCRIPTION:
+ SetDParamStr(0, BaseMusic::GetUsedSet()->GetDescription(GetCurrentLanguageIsoCode()));
+ DrawStringMultiLine(r.left, r.right, r.top, UINT16_MAX, STR_BLACK_RAW_STRING);
+ break;
}
}
@@ -246,6 +256,25 @@ struct GameOptionsWindow : Window {
size->height = max(size->height, (uint)GetStringHeight(STR_BLACK_RAW_STRING, size->width));
}
break;
+
+ case GOW_BASE_MUSIC_DESCRIPTION:
+ /* Find the biggest description for the default size. */
+ for (int i = 0; i < BaseMusic::GetNumSets(); i++) {
+ SetDParamStr(0, BaseMusic::GetSet(i)->GetDescription(GetCurrentLanguageIsoCode()));
+ size->height = max(size->height, (uint)GetStringHeight(STR_BLACK_RAW_STRING, size->width));
+ }
+ break;
+
+ case GOW_BASE_MUSIC_STATUS:
+ /* Find the biggest description for the default size. */
+ for (int i = 0; i < BaseMusic::GetNumSets(); i++) {
+ uint invalid_files = BaseMusic::GetSet(i)->GetNumInvalid();
+ if (invalid_files == 0) continue;
+
+ SetDParam(0, invalid_files);
+ *size = maxdim(*size, GetStringBoundingBox(STR_GAME_OPTIONS_BASE_MUSIC_STATUS));
+ }
+ break;
}
}
@@ -320,6 +349,10 @@ struct GameOptionsWindow : Window {
case GOW_BASE_SFX_DROPDOWN:
ShowSetMenu<BaseSounds>(this, GOW_BASE_SFX_DROPDOWN);
break;
+
+ case GOW_BASE_MUSIC_DROPDOWN:
+ ShowSetMenu<BaseMusic>(this, GOW_BASE_MUSIC_DROPDOWN);
+ break;
}
}
@@ -403,6 +436,10 @@ struct GameOptionsWindow : Window {
case GOW_BASE_SFX_DROPDOWN:
this->SetMediaSet<BaseSounds>(index);
break;
+
+ case GOW_BASE_MUSIC_DROPDOWN:
+ this->SetMediaSet<BaseMusic>(index);
+ break;
}
}
@@ -412,6 +449,9 @@ struct GameOptionsWindow : Window {
bool missing_files = BaseGraphics::GetUsedSet()->GetNumMissing() == 0;
this->GetWidget<NWidgetCore>(GOW_BASE_GRF_STATUS)->SetDataTip(missing_files ? STR_EMPTY : STR_GAME_OPTIONS_BASE_GRF_STATUS, STR_NULL);
+
+ missing_files = BaseMusic::GetUsedSet()->GetNumInvalid() == 0;
+ this->GetWidget<NWidgetCore>(GOW_BASE_MUSIC_STATUS)->SetDataTip(missing_files ? STR_EMPTY : STR_GAME_OPTIONS_BASE_MUSIC_STATUS, STR_NULL);
}
};
@@ -459,7 +499,7 @@ static const NWidgetPart _nested_game_options_widgets[] = {
EndContainer(),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_BASE_GRF, STR_NULL), SetPadding(0, 10, 0, 10),
- NWidget(NWID_HORIZONTAL), SetPIP(00, 30, 0),
+ NWidget(NWID_HORIZONTAL), SetPIP(0, 30, 0),
NWidget(WWT_DROPDOWN, COLOUR_GREY, GOW_BASE_GRF_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_BASE_GRF_TOOLTIP),
NWidget(WWT_TEXT, COLOUR_GREY, GOW_BASE_GRF_STATUS), SetMinimalSize(150, 12), SetDataTip(STR_EMPTY, STR_NULL), SetFill(1, 0),
EndContainer(),
@@ -473,6 +513,14 @@ static const NWidgetPart _nested_game_options_widgets[] = {
EndContainer(),
NWidget(WWT_TEXT, COLOUR_GREY, GOW_BASE_SFX_DESCRIPTION), SetMinimalSize(330, 0), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_BASE_SFX_DESCRIPTION_TOOLTIP), SetFill(1, 0), SetPadding(6, 0, 0, 0),
EndContainer(),
+
+ NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_BASE_MUSIC, STR_NULL), SetPadding(0, 10, 0, 10),
+ NWidget(NWID_HORIZONTAL), SetPIP(0, 30, 0),
+ NWidget(WWT_DROPDOWN, COLOUR_GREY, GOW_BASE_MUSIC_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(STR_BLACK_RAW_STRING, STR_GAME_OPTIONS_BASE_MUSIC_TOOLTIP),
+ NWidget(WWT_TEXT, COLOUR_GREY, GOW_BASE_MUSIC_STATUS), SetMinimalSize(150, 12), SetDataTip(STR_EMPTY, STR_NULL), SetFill(1, 0),
+ EndContainer(),
+ NWidget(WWT_TEXT, COLOUR_GREY, GOW_BASE_MUSIC_DESCRIPTION), SetMinimalSize(330, 0), SetDataTip(STR_EMPTY, STR_GAME_OPTIONS_BASE_MUSIC_DESCRIPTION_TOOLTIP), SetFill(1, 0), SetPadding(6, 0, 0, 0),
+ EndContainer(),
EndContainer(),
};
diff --git a/src/sound.cpp b/src/sound.cpp
index 3b81af08b..1fc5291c3 100644
--- a/src/sound.cpp
+++ b/src/sound.cpp
@@ -282,8 +282,8 @@ INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<SoundsSet>, SoundsSet)
static const char * const _sound_file_names[] = { "samples" };
-template <class T, size_t Tnum_files>
-/* static */ const char * const *BaseSet<T, Tnum_files>::file_names = _sound_file_names;
+template <class T, size_t Tnum_files, Subdirectory Tsubdir>
+/* static */ const char * const *BaseSet<T, Tnum_files, Tsubdir>::file_names = _sound_file_names;
template <class Tbase_set>
/* static */ const char *BaseMedia<Tbase_set>::GetExtension()
diff --git a/src/table/settings.h b/src/table/settings.h
index 11a097d38..8c2e6b5e1 100644
--- a/src/table/settings.h
+++ b/src/table/settings.h
@@ -249,6 +249,7 @@ static const SettingDescGlobVarList _misc_settings[] = {
SDTG_BOOL("fullscreen", S, 0, _fullscreen, false, STR_NULL, NULL),
SDTG_STR("graphicsset", SLE_STRQ, S, 0, BaseGraphics::ini_set, NULL, STR_NULL, NULL),
SDTG_STR("soundsset", SLE_STRQ, S, 0, BaseSounds::ini_set, NULL, STR_NULL, NULL),
+ SDTG_STR("musicset", SLE_STRQ, S, 0, BaseMusic::ini_set, NULL, STR_NULL, NULL),
SDTG_STR("videodriver", SLE_STRQ, S, 0, _ini_videodriver, NULL, STR_NULL, NULL),
SDTG_STR("musicdriver", SLE_STRQ, S, 0, _ini_musicdriver, NULL, STR_NULL, NULL),
SDTG_STR("sounddriver", SLE_STRQ, S, 0, _ini_sounddriver, NULL, STR_NULL, NULL),