summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/gm/no_music.obm48
-rw-r--r--bin/gm/orig_win.obm94
-rw-r--r--docs/obm_format.txt106
-rw-r--r--os/windows/installer/install.nsi8
-rw-r--r--projects/openttd_vs80.vcproj4
-rw-r--r--projects/openttd_vs90.vcproj4
-rw-r--r--source.list1
-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
21 files changed, 602 insertions, 134 deletions
diff --git a/bin/gm/no_music.obm b/bin/gm/no_music.obm
new file mode 100644
index 000000000..0a325bede
--- /dev/null
+++ b/bin/gm/no_music.obm
@@ -0,0 +1,48 @@
+; $Id$
+;
+; This represents more or less nothingness
+;
+[metadata]
+name = NoMusic
+shortname = NOMU
+version = 0
+description = A music pack without actual music
+
+[files]
+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 =
+
+[md5s]
+
+[names]
+
+[origin]
diff --git a/bin/gm/orig_win.obm b/bin/gm/orig_win.obm
new file mode 100644
index 000000000..72b041161
--- /dev/null
+++ b/bin/gm/orig_win.obm
@@ -0,0 +1,94 @@
+; $Id$
+;
+; This represents the original music as on the Transport
+; Tycoon Deluxe for Windows CD.
+;
+[metadata]
+name = original_windows
+shortname = TTDW
+version = 1
+description = Original Transport Tycoon Deluxe Windows edition music
+
+[files]
+theme = GM_TT00.GM
+old_0 = GM_TT02.GM
+old_1 = GM_TT06.GM
+old_2 = GM_TT03.GM
+old_3 = GM_TT12.GM
+old_4 = GM_TT08.GM
+old_5 = GM_TT13.GM
+old_6 = GM_TT14.GM
+old_7 = GM_TT19.GM
+old_8 =
+old_9 =
+new_0 = GM_TT04.GM
+new_1 = GM_TT01.GM
+new_2 = GM_TT05.GM
+new_3 = GM_TT15.GM
+new_4 = GM_TT11.GM
+new_5 = GM_TT16.GM
+new_6 = GM_TT09.GM
+new_7 =
+new_8 =
+new_9 =
+ezy_0 = GM_TT18.GM
+ezy_1 = GM_TT19.GM
+ezy_2 = GM_TT21.GM
+ezy_3 = GM_TT17.GM
+ezy_4 = GM_TT20.GM
+ezy_5 = GM_TT07.GM
+ezy_6 =
+ezy_7 =
+ezy_8 =
+ezy_9 =
+
+[md5s]
+GM_TT00.GM = 45cfec1b9d8c7a0ad45e755833cbf221
+GM_TT01.GM = ab14ed3392d848abd2a2e90a9d75d121
+GM_TT02.GM = dd4f696e4be5987ce738257b08b50171
+GM_TT03.GM = a1bfde23343df9e4063419bf29c166b8
+GM_TT04.GM = 4e6943aa0c455203d76c79389054747d
+GM_TT05.GM = cee281cb85a2e2343552d97640545a47
+GM_TT06.GM = 26d1de5efa8675f94065784e9d539e49
+GM_TT07.GM = 6f2691e17558f552ec4c565e4ab7139c
+GM_TT08.GM = a42bf2cb3340a822f1a69646fc7a487d
+GM_TT09.GM = eb35761a58a8df3c59ed8929cce13916
+GM_TT10.GM = 42fecd686720a785d20a78590c466a82
+GM_TT11.GM = 50ef1ef02e49d2112786dd45e69dc3ee
+GM_TT12.GM = 4ce707a0e0e72419f0681dd9bd95271b
+GM_TT13.GM = e765753be29d889ec818f38009103619
+GM_TT14.GM = 270e2d63bd32b95a4d007ce15a6ce45f
+GM_TT15.GM = 89e116a1c0c69f1845cc903a9bfbe460
+GM_TT16.GM = f824e2371b3bedfe61aad4b9c62dd6be
+GM_TT17.GM = 1b23eebb0796c1ab99cd97fa7082cf7b
+GM_TT18.GM = 15650de3bad645d0e88c4f5c7a2df92a
+GM_TT19.GM = 7aec079e15bd09588660b85545ac4dfc
+GM_TT20.GM = 1509097889dee617aa1e9a1738a5a930
+GM_TT21.GM = a8d0aaad02e1a762d8d54cf81da56bab
+
+[names]
+GM_TT00.GM = Tycoon DELUXE Theme
+GM_TT01.GM = Snarl Up
+GM_TT02.GM = Easy Driver
+GM_TT03.GM = Little Red Diesel
+GM_TT04.GM = City Groove
+GM_TT05.GM = Aliens Ate My Railway
+GM_TT06.GM = Stoke It
+GM_TT07.GM = Don't Walk!
+GM_TT08.GM = Sawyer's Tune
+GM_TT09.GM = Fell Apart On Me
+GM_TT10.GM = Can't Get There From Here
+GM_TT11.GM = Hard Drivin'
+GM_TT12.GM = Road Hog
+GM_TT13.GM = Hold That Train!
+GM_TT14.GM = Broomer's Oil Rag
+GM_TT15.GM = Goss Groove
+GM_TT16.GM = Small Town
+GM_TT17.GM = Cruise Control
+GM_TT18.GM = Stroll On
+GM_TT19.GM = Funk Central
+GM_TT20.GM = Jammit
+GM_TT21.GM = Movin' On
+
+[origin]
+default = You can find it on your Transport Tycoon Deluxe CD-ROM.
diff --git a/docs/obm_format.txt b/docs/obm_format.txt
new file mode 100644
index 000000000..40f829a52
--- /dev/null
+++ b/docs/obm_format.txt
@@ -0,0 +1,106 @@
+;
+; Example file for the OpenTTD Base Music replacement sets.
+; This file consists of basically two different parts:
+; * metadata
+; * information about the files/songs
+;
+; Metadata contains information about the name and version
+; of the music set.
+;
+; == Getting started ==
+; - you can't add comments after values
+; - you have to fill the MD5 checksum for each file
+; - you may not miss any of the metadata or files items
+; - `openttd -h` lists all music replacement sets it found to be correct
+; - `openttd -d grf=1` shows warnings/errors when parsing an .obm file
+; - `openttd -M <name>` starts OpenTTD with the given set (case sensitive)
+; - adding `musicset = <name>` to the misc section of openttd.cfg makes
+; OpenTTD start with that sound set by default
+; - there is a command line tool for all platforms called md5sum that can
+; create the MD5 checksum you need.
+; - all files specified in this file are search relatively to the path where
+; this file is found, i.e. if the sound files are in a subdir you have
+; to add that subdir to the names in this file to! It will NOT search for
+; a file named like specified in here.
+
+[metadata]
+; the name of the pack, preferably less than 16 characters
+name = example
+; the short name (4 characters), used to identify this set
+shortname = XMPL
+; the version of this sound set (read as single integer)
+version = 0
+; a fairly short description of the set
+; By adding '.<iso code>' you can translate the description.
+; Note that OpenTTD first tries the full ISO code, then the first
+; two characters and then uses the fallback (no '.<iso code>').
+; The ISO code matching is case sensitive!
+; So en_US will be used for en_GB if no en_GB translation is added.
+; As a result the below example has 'howdie' for en_US and en_GB but
+; 'foo' for all other languages.
+description = foo
+description.en_US = howdie
+
+; The files section lists the files that replace songs.
+; The file names are case sensitive.
+; You can have empty file names; in that case no song will be loaded
+; for that 'entry'.
+[files]
+; The theme song for OpenTTD
+theme = THEME_SONG.GM
+; The songs in the 'old style' category
+old_0 =
+old_1 =
+old_2 =
+old_3 =
+old_4 =
+old_5 =
+old_6 =
+old_7 =
+old_8 =
+old_9 =
+; The songs in the 'new style' category
+new_0 =
+new_1 =
+new_2 =
+new_3 =
+new_4 =
+new_5 =
+new_6 =
+new_7 =
+new_8 =
+new_9 =
+; The songs in the 'ezy street' category
+ezy_0 =
+ezy_1 =
+ezy_2 =
+ezy_3 =
+ezy_4 =
+ezy_5 =
+ezy_6 =
+ezy_7 =
+ezy_8 =
+ezy_9 =
+
+; The names section lists the song names for the given file name.
+; Note that the list of files is case sensitive. Each file listed in the
+; files section must be listed here with it's song name, otherwise you
+; will get a lot of warnings when starting OpenTTD.
+[names]
+THEME_SONG.GM = Tycoon DELUXE Theme
+
+; The md5s section lists the MD5 checksum for the files that replace them.
+; Note that the list of files is case sensitive. Each file listed in the
+; files section must be listed here with it's MD5 checksum, otherwise you
+; will get a lot of warnings when starting OpenTTD.
+[md5s]
+THEME_SONG.GM = 45cfec1b9d8c7a0ad45e755833cbf221
+
+; The origin section provides the possibility to put and extra line into
+; the warning that a file is missing/corrupt. This can be used to tell
+; them where to find it. It works on the filename specified in the
+; files section and if that is not found it will fall back to the default
+; as shown below here.
+[origin]
+default = You can find it on your Transport Tycoon Deluxe CD-ROM.
+THEME_SONG.GM = You can find it also on your Transport Tycoon Deluxe CD-ROM.
diff --git a/os/windows/installer/install.nsi b/os/windows/installer/install.nsi
index 84973ca44..120215067 100644
--- a/os/windows/installer/install.nsi
+++ b/os/windows/installer/install.nsi
@@ -123,6 +123,10 @@ Section "!OpenTTD" Section1
File ${PATH_ROOT}bin\data\*.obs
File ${PATH_ROOT}bin\data\opntitle.dat
+ ; Copy the music base metadata files
+ SetOutPath "$INSTDIR\gm\"
+ File ${PATH_ROOT}bin\gm\*.obm
+
; Copy the scripts
SetOutPath "$INSTDIR\scripts\"
File ${PATH_ROOT}bin\scripts\*.*
@@ -397,6 +401,10 @@ Section "Uninstall"
; Scripts
Delete "$INSTDIR\scripts\*.*"
+ ; Base sets for music
+ Delete "$INSTDIR\gm\orig_mus.obm"
+ Delete "$INSTDIR\gm\no_sound.obm"
+
; Remove remaining directories
RMDir "$SMPROGRAMS\$SHORTCUTS\Extras\"
RMDir "$SMPROGRAMS\$SHORTCUTS"
diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj
index 049536a2f..8e4934153 100644
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -1100,10 +1100,6 @@
>
</File>
<File
- RelativePath=".\..\src\music.h"
- >
- </File>
- <File
RelativePath=".\..\src\network\network.h"
>
</File>
diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj
index d32ec7361..a4e6376a9 100644
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -1097,10 +1097,6 @@
>
</File>
<File
- RelativePath=".\..\src\music.h"
- >
- </File>
- <File
RelativePath=".\..\src\network\network.h"
>
</File>
diff --git a/source.list b/source.list
index 9f70b0016..4c0153e0b 100644
--- a/source.list
+++ b/source.list
@@ -184,7 +184,6 @@ livery.h
map_func.h
map_type.h
mixer.h
-music.h
network/network.h
network/network_base.h
network/network_client.h
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),