(svn r18608) -Change: add the concept of music sets

replace/41b28d7194a279bdc17475d4fbe2ea6ec885a466
rubidium 15 years ago
parent b30e56850c
commit a8d6d18b22

@ -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]

@ -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.

@ -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.

@ -123,6 +123,10 @@ Section "!OpenTTD" Section1
File ${PATH_ROOT}bin\data\*.obs File ${PATH_ROOT}bin\data\*.obs
File ${PATH_ROOT}bin\data\opntitle.dat 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 ; Copy the scripts
SetOutPath "$INSTDIR\scripts\" SetOutPath "$INSTDIR\scripts\"
File ${PATH_ROOT}bin\scripts\*.* File ${PATH_ROOT}bin\scripts\*.*
@ -397,6 +401,10 @@ Section "Uninstall"
; Scripts ; Scripts
Delete "$INSTDIR\scripts\*.*" Delete "$INSTDIR\scripts\*.*"
; Base sets for music
Delete "$INSTDIR\gm\orig_mus.obm"
Delete "$INSTDIR\gm\no_sound.obm"
; Remove remaining directories ; Remove remaining directories
RMDir "$SMPROGRAMS\$SHORTCUTS\Extras\" RMDir "$SMPROGRAMS\$SHORTCUTS\Extras\"
RMDir "$SMPROGRAMS\$SHORTCUTS" RMDir "$SMPROGRAMS\$SHORTCUTS"

@ -1099,10 +1099,6 @@
RelativePath=".\..\src\mixer.h" RelativePath=".\..\src\mixer.h"
> >
</File> </File>
<File
RelativePath=".\..\src\music.h"
>
</File>
<File <File
RelativePath=".\..\src\network\network.h" RelativePath=".\..\src\network\network.h"
> >

@ -1096,10 +1096,6 @@
RelativePath=".\..\src\mixer.h" RelativePath=".\..\src\mixer.h"
> >
</File> </File>
<File
RelativePath=".\..\src\music.h"
>
</File>
<File <File
RelativePath=".\..\src\network\network.h" RelativePath=".\..\src\network\network.h"
> >

@ -184,7 +184,6 @@ livery.h
map_func.h map_func.h
map_type.h map_type.h
mixer.h mixer.h
music.h
network/network.h network/network.h
network/network_base.h network/network_base.h
network/network_client.h network/network_client.h

@ -14,6 +14,7 @@
#include "fileio_func.h" #include "fileio_func.h"
#include "core/smallmap_type.hpp" #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 */ /* Forward declare these; can't do 'struct X' in functions as older GCCs barf on that */
struct IniFile; struct IniFile;
@ -32,21 +33,25 @@ struct MD5File {
uint8 hash[16]; ///< md5 sum of the file uint8 hash[16]; ///< md5 sum of the file
const char *missing_warning; ///< warning when this file is missing const char *missing_warning; ///< warning when this file is missing
ChecksumResult CheckMD5() const; ChecksumResult CheckMD5(Subdirectory subdir) const;
}; };
/** /**
* Information about a single base set. * Information about a single base set.
* @tparam T the real class we're going to be * @tparam T the real class we're going to be
* @tparam Tnum_files the number of files in the set * @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 { struct BaseSet {
typedef SmallMap<const char *, const char *> TranslatedStrings; typedef SmallMap<const char *, const char *> TranslatedStrings;
/** Number of files in this set */ /** Number of files in this set */
static const size_t NUM_FILES = Tnum_files; 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. */ /** Internal names of the files in this set. */
static const char * const *file_names; static const char * const *file_names;
@ -163,7 +168,7 @@ public:
static uint FindSets() static uint FindSets()
{ {
BaseMedia<Tbase_set> fs; 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. */ /** 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 PaletteType palette; ///< Palette of this graphics set
bool FillSetDetails(struct IniFile *ini, const char *path); bool FillSetDetails(struct IniFile *ini, const char *path);
@ -242,7 +247,7 @@ public:
}; };
/** All data of a sounds set. */ /** 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 */ /** All data/functions related with replacing the base sounds */
@ -250,4 +255,29 @@ class BaseSounds : public BaseMedia<SoundsSet> {
public: 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 */ #endif /* BASE_MEDIA_BASE_H */

@ -28,8 +28,8 @@ template <class Tbase_set> /* static */ Tbase_set *BaseMedia<Tbase_set>::availab
return false; \ return false; \
} }
template <class T, size_t Tnum_files> template <class T, size_t Tnum_files, Subdirectory Tsubdir>
bool BaseSet<T, Tnum_files>::FillSetDetails(IniFile *ini, const char *path) bool BaseSet<T, Tnum_files, Tsubdir>::FillSetDetails(IniFile *ini, const char *path)
{ {
memset(this, 0, sizeof(*this)); 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++) { for (uint i = 0; i < Tnum_files; i++) {
MD5File *file = &this->files[i]; MD5File *file = &this->files[i];
/* Find the filename first. */ /* 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) { 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; return false;
} }
const char *filename = item->value; 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); file->filename = str_fmt("%s%s", path, filename);
/* Then find the MD5 checksum */ /* 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 */ /* 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) item = origin->GetItem("default", false);
if (item == NULL) { if (item == NULL) {
DEBUG(grf, 1, "No origin warning message specified for: %s", filename); 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); file->missing_warning = strdup(item->value);
} }
switch (file->CheckMD5()) { switch (file->CheckMD5(Tsubdir)) {
case MD5File::CR_MATCH: case MD5File::CR_MATCH:
this->valid_files++; this->valid_files++;
/* FALL THROUGH */ /* FALL THROUGH */

@ -1669,7 +1669,7 @@ DEF_CONSOLE_CMD(ConContent)
if (strcasecmp(argv[1], "state") == 0) { if (strcasecmp(argv[1], "state") == 0) {
IConsolePrintF(CC_WHITE, "id, type, state, name"); IConsolePrintF(CC_WHITE, "id, type, state, name");
for (ConstContentIterator iter = _network_content_client.Begin(); iter != _network_content_client.End(); iter++) { 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); assert_compile(lengthof(types) == CONTENT_TYPE_END - CONTENT_TYPE_BEGIN);
static const char * const states[] = { "Not selected", "Selected", "Dep Selected", "Installed", "Unknown" }; 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 }; static const ConsoleColour state_to_colour[] = { CC_COMMAND, CC_INFO, CC_INFO, CC_WHITE, CC_ERROR };

@ -118,7 +118,7 @@ void CheckExternalFiles()
/* Not all files were loaded succesfully, see which ones */ /* 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); 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++) { 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); 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"); add_pos += seprintf(add_pos, last, "\n");
@ -131,7 +131,7 @@ void CheckExternalFiles()
assert_compile(SoundsSet::NUM_FILES == 1); assert_compile(SoundsSet::NUM_FILES == 1);
/* No need to loop each file, as long as there is only a single /* No need to loop each file, as long as there is only a single
* sound file. */ * 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); if (add_pos != error_msg) ShowInfoF("%s", error_msg);
@ -206,7 +206,7 @@ void GfxLoadSprites()
bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path) 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) { if (ret) {
IniGroup *metadata = ini->GetGroup("metadata"); IniGroup *metadata = ini->GetGroup("metadata");
IniItem *item; IniItem *item;
@ -220,15 +220,16 @@ bool GraphicsSet::FillSetDetails(IniFile *ini, const char *path)
/** /**
* Calculate and check the MD5 hash of the supplied filename. * Calculate and check the MD5 hash of the supplied filename.
* @param subdir The sub directory to get the files from
* @return * @return
* CR_MATCH if the MD5 hash matches * CR_MATCH if the MD5 hash matches
* CR_MISMATCH if the MD5 does not match * CR_MISMATCH if the MD5 does not match
* CR_NO_FILE if the file misses * CR_NO_FILE if the file misses
*/ */
MD5File::ChecksumResult MD5File::CheckMD5() const MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir) const
{ {
size_t size; 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; 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" }; static const char * const _graphics_file_names[] = { "base", "logos", "arctic", "tropical", "toyland", "extra" };
/** Implementation */ /** Implementation */
template <class T, size_t Tnum_files> template <class T, size_t Tnum_files, Subdirectory Tsubdir>
/* static */ const char * const *BaseSet<T, Tnum_files>::file_names = _graphics_file_names; /* static */ const char * const *BaseSet<T, Tnum_files, Tsubdir>::file_names = _graphics_file_names;
extern void UpdateNewGRFConfigPalette(); extern void UpdateNewGRFConfigPalette();

@ -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_TOGGLE_PROGRAM_SHUFFLE :{BLACK}Toggle programme shuffle on/off
STR_MUSIC_TOOLTIP_SHOW_MUSIC_TRACK_SELECTION :{BLACK}Show music track selection window 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 # Playlist window
STR_PLAYLIST_MUSIC_PROGRAM_SELECTION :{WHITE}Music Programme Selection STR_PLAYLIST_MUSIC_PROGRAM_SELECTION :{WHITE}Music Programme Selection
STR_PLAYLIST_TRACK_NAME :{TINYFONT}{LTBLUE}{ZEROFILL_NUM} "{RAW_STRING}" 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_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_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 STR_ERROR_FULLSCREEN_FAILED :{WHITE}Fullscreen mode failed
# Custom currency window # Custom currency window
@ -1812,6 +1819,7 @@ STR_CONTENT_TYPE_AI_LIBRARY :AI library
STR_CONTENT_TYPE_SCENARIO :Scenario STR_CONTENT_TYPE_SCENARIO :Scenario
STR_CONTENT_TYPE_HEIGHTMAP :Heightmap STR_CONTENT_TYPE_HEIGHTMAP :Heightmap
STR_CONTENT_TYPE_BASE_SOUNDS :Base sounds STR_CONTENT_TYPE_BASE_SOUNDS :Base sounds
STR_CONTENT_TYPE_BASE_MUSIC :Base music
# Content downloading progress window # Content downloading progress window
STR_CONTENT_DOWNLOAD_TITLE :{WHITE}Downloading content... STR_CONTENT_DOWNLOAD_TITLE :{WHITE}Downloading content...

@ -31,6 +31,7 @@ extern TileIndex _cur_tileloop_tile;
extern void MakeNewgameSettingsLive(); extern void MakeNewgameSettingsLive();
void InitializeSound(); void InitializeSound();
void InitializeMusic();
void InitializeVehicles(); void InitializeVehicles();
void InitializeDepots(); void InitializeDepots();
void InitializeEngineRenews(); void InitializeEngineRenews();
@ -71,6 +72,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
if (reset_settings) MakeNewgameSettingsLive(); if (reset_settings) MakeNewgameSettingsLive();
InitializeSound(); InitializeSound();
InitializeMusic();
if (reset_date) { if (reset_date) {
SetDate(ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1)); SetDate(ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1));

@ -10,31 +10,85 @@
/** @file music.cpp The songs that OpenTTD knows. */ /** @file music.cpp The songs that OpenTTD knows. */
#include "stdafx.h" #include "stdafx.h"
#include "music.h" #include "debug.h"
const SongSpecs _origin_songs_specs[] = { /* The type of set we're replacing */
{"gm_tt00.gm", "Tycoon DELUXE Theme"}, #define SET_TYPE "music"
{"gm_tt02.gm", "Easy Driver"}, #include "base_media_func.h"
{"gm_tt03.gm", "Little Red Diesel"},
{"gm_tt17.gm", "Cruise Control"}, INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<MusicSet>, MusicSet)
{"gm_tt07.gm", "Don't Walk!"},
{"gm_tt09.gm", "Fell Apart On Me"}, /** Names corresponding to the music set's files */
{"gm_tt04.gm", "City Groove"}, static const char * const _music_file_names[] = {
{"gm_tt19.gm", "Funk Central"}, "theme",
{"gm_tt06.gm", "Stoke It"}, "old_0", "old_1", "old_2", "old_3", "old_4", "old_5", "old_6", "old_7", "old_8", "old_9",
{"gm_tt12.gm", "Road Hog"}, "new_0", "new_1", "new_2", "new_3", "new_4", "new_5", "new_6", "new_7", "new_8", "new_9",
{"gm_tt05.gm", "Aliens Ate My Railway"}, "ezy_0", "ezy_1", "ezy_2", "ezy_3", "ezy_4", "ezy_5", "ezy_6", "ezy_7", "ezy_8", "ezy_9",
{"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'"},
}; };
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;
}

@ -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 */

@ -12,7 +12,7 @@
#include "stdafx.h" #include "stdafx.h"
#include "openttd.h" #include "openttd.h"
#include "fileio_func.h" #include "fileio_func.h"
#include "music.h" #include "base_media_base.h"
#include "music/music_driver.hpp" #include "music/music_driver.hpp"
#include "window_gui.h" #include "window_gui.h"
#include "strings_func.h" #include "strings_func.h"
@ -20,6 +20,7 @@
#include "sound_func.h" #include "sound_func.h"
#include "gfx_func.h" #include "gfx_func.h"
#include "core/random_func.hpp" #include "core/random_func.hpp"
#include "gui.h"
#include "table/strings.h" #include "table/strings.h"
#include "table/sprites.h" #include "table/sprites.h"
@ -31,32 +32,40 @@
*/ */
static const char *GetSongName(int index) 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; /** The currently played song */
static bool _song_is_active; static byte _music_wnd_cursong = 1;
static byte _cur_playlist[NUM_SONGS_PLAYLIST]; /** 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[] = { /** Indices of all songs */
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0 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[] = { assert_compile(lengthof(msf.custom_1) == NUM_SONGS_PLAYLIST + 1);
2, 9, 3, 10, 15, 16, 20, 14, 0 assert_compile(lengthof(msf.custom_2) == NUM_SONGS_PLAYLIST + 1);
};
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
};
/** The different playlists that can be played. */
static byte * const _playlists[] = { static byte * const _playlists[] = {
_playlist_all, _playlist_all,
_playlist_old_style, _playlist_old_style,
@ -66,6 +75,57 @@ static byte * const _playlists[] = {
msf.custom_2, 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() static void SkipToPrevSong()
{ {
byte *b = _cur_playlist; byte *b = _cur_playlist;
@ -112,13 +172,15 @@ static void DoPlaySong()
{ {
char filename[MAX_PATH]; char filename[MAX_PATH];
FioFindFullPath(filename, lengthof(filename), GM_DIR, 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); _music_driver->PlaySong(filename);
SetWindowDirty(WC_MUSIC_WINDOW, 0);
} }
static void DoStopMusic() static void DoStopMusic()
{ {
_music_driver->StopSong(); _music_driver->StopSong();
SetWindowDirty(WC_MUSIC_WINDOW, 0);
} }
static void SelectSongToPlay() static void SelectSongToPlay()
@ -128,9 +190,10 @@ static void SelectSongToPlay()
memset(_cur_playlist, 0, sizeof(_cur_playlist)); memset(_cur_playlist, 0, sizeof(_cur_playlist));
do { do {
const char *filename = BaseMusic::GetUsedSet()->files[_playlists[msf.playlist][i] - 1].filename;
/* We are now checking for the existence of that file prior /* We are now checking for the existence of that file prior
* to add it to the list of available songs */ * 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]; _cur_playlist[j] = _playlists[msf.playlist][i];
j++; j++;
} }
@ -275,10 +338,13 @@ struct MusicTrackSelectionWindow : public Window {
case MTSW_LIST_LEFT: case MTSW_LIST_RIGHT: { case MTSW_LIST_LEFT: case MTSW_LIST_RIGHT: {
Dimension d = {0, 0}; Dimension d = {0, 0};
for (uint i = 1; i <= NUM_SONGS_AVAILABLE; i++) { for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
SetDParam(0, i); const char *song_name = GetSongName(i);
if (StrEmpty(song_name)) continue;
SetDParam(0, GetTrackNumber(i));
SetDParam(1, 2); SetDParam(1, 2);
SetDParamStr(2, GetSongName(i - 1)); SetDParamStr(2, GetSongName(i));
Dimension d2 = GetStringBoundingBox(STR_PLAYLIST_TRACK_NAME); Dimension d2 = GetStringBoundingBox(STR_PLAYLIST_TRACK_NAME);
d.width = max(d.width, d2.width); d.width = max(d.width, d2.width);
d.height += d2.height; 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); GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, 0);
int y = r.top + WD_FRAMERECT_TOP; int y = r.top + WD_FRAMERECT_TOP;
for (uint i = 1; i <= NUM_SONGS_AVAILABLE; i++) { for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
SetDParam(0, i); const char *song_name = GetSongName(i);
if (StrEmpty(song_name)) continue;
SetDParam(0, GetTrackNumber(i));
SetDParam(1, 2); 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); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
y += FONT_HEIGHT_SMALL; y += FONT_HEIGHT_SMALL;
} }
@ -311,10 +380,10 @@ struct MusicTrackSelectionWindow : public Window {
int y = r.top + WD_FRAMERECT_TOP; int y = r.top + WD_FRAMERECT_TOP;
for (const byte *p = _playlists[msf.playlist]; *p != 0; p++) { for (const byte *p = _playlists[msf.playlist]; *p != 0; p++) {
uint i = *p; uint i = *p - 1;
SetDParam(0, i); SetDParam(0, GetTrackNumber(i));
SetDParam(1, 2); 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); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
y += FONT_HEIGHT_SMALL; 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; int y = (pt.y - this->GetWidget<NWidgetBase>(widget)->pos_y) / FONT_HEIGHT_SMALL;
if (msf.playlist < 4) return; 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]; byte *p = _playlists[msf.playlist];
for (uint i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) { for (uint i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) {
if (p[i] == 0) { 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; p[i + 1] = 0;
this->SetDirty(); this->SetDirty();
SelectSongToPlay(); SelectSongToPlay();
@ -352,7 +427,7 @@ struct MusicTrackSelectionWindow : public Window {
int y = (pt.y - this->GetWidget<NWidgetBase>(widget)->pos_y) / FONT_HEIGHT_SMALL; int y = (pt.y - this->GetWidget<NWidgetBase>(widget)->pos_y) / FONT_HEIGHT_SMALL;
if (msf.playlist < 4) return; 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]; byte *p = _playlists[msf.playlist];
for (uint i = y; i != NUM_SONGS_PLAYLIST - 1; i++) { for (uint i = y; i != NUM_SONGS_PLAYLIST - 1; i++) {
@ -364,7 +439,7 @@ struct MusicTrackSelectionWindow : public Window {
} break; } break;
case MTSW_CLEAR: // clear 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(); this->SetDirty();
StopMusic(); StopMusic();
SelectSongToPlay(); SelectSongToPlay();
@ -484,7 +559,7 @@ struct MusicWindow : public Window {
case MW_TRACK_NAME: { case MW_TRACK_NAME: {
Dimension d = GetStringBoundingBox(STR_MUSIC_TITLE_NONE); 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)); SetDParamStr(0, GetSongName(i));
d = maxdim(d, GetStringBoundingBox(STR_MUSIC_TITLE_NAME)); 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); GfxFillRect(r.left + 1, r.top + 1, r.right, r.bottom, 0);
StringID str = STR_MUSIC_TRACK_NONE; StringID str = STR_MUSIC_TRACK_NONE;
if (_song_is_active != 0 && _music_wnd_cursong != 0) { if (_song_is_active != 0 && _music_wnd_cursong != 0) {
SetDParam(0, _music_wnd_cursong); SetDParam(0, GetTrackNumber(_music_wnd_cursong - 1));
SetDParam(1, 2); SetDParam(1, 2);
str = STR_MUSIC_TRACK_DIGIT; str = STR_MUSIC_TRACK_DIGIT;
} }
@ -727,5 +802,6 @@ static const WindowDesc _music_window_desc(
void ShowMusicWindow() void ShowMusicWindow()
{ {
if (BaseMusic::GetUsedSet()->num_available == 0) ShowErrorMessage(STR_ERROR_NO_SONGS, INVALID_STRING_ID, 0, 0);
AllocateWindowDescFront<MusicWindow>(&_music_window_desc, 0); AllocateWindowDescFront<MusicWindow>(&_music_window_desc, 0);
} }

@ -31,6 +31,7 @@ enum ContentType {
CONTENT_TYPE_SCENARIO = 5, ///< The content consists of a scenario CONTENT_TYPE_SCENARIO = 5, ///< The content consists of a scenario
CONTENT_TYPE_HEIGHTMAP = 6, ///< The content consists of a heightmap CONTENT_TYPE_HEIGHTMAP = 6, ///< The content consists of a heightmap
CONTENT_TYPE_BASE_SOUNDS = 7, ///< The content consists of base sounds 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 CONTENT_TYPE_END, ///< Helper to mark the end of the types
}; };

@ -181,6 +181,7 @@ static void ShowHelp()
" specified in graphics set file (see below)\n" " specified in graphics set file (see below)\n"
" -I graphics_set = Force the graphics set (see below)\n" " -I graphics_set = Force the graphics set (see below)\n"
" -S sounds_set = Force the sounds 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" " -c config_file = Use 'config_file' instead of 'openttd.cfg'\n"
" -x = Do not automatically save to config file on exit\n" " -x = Do not automatically save to config file on exit\n"
"\n", "\n",
@ -193,6 +194,9 @@ static void ShowHelp()
/* List the sounds packs */ /* List the sounds packs */
p = BaseSounds::GetSetsList(p, lastof(buf)); p = BaseSounds::GetSetsList(p, lastof(buf));
/* List the music packs */
p = BaseMusic::GetSetsList(p, lastof(buf));
/* List the drivers */ /* List the drivers */
p = VideoDriverFactoryBase::GetDriversInfo(p, lastof(buf)); p = VideoDriverFactoryBase::GetDriversInfo(p, lastof(buf));
@ -409,6 +413,7 @@ int ttd_main(int argc, char *argv[])
char *blitter = NULL; char *blitter = NULL;
char *graphics_set = NULL; char *graphics_set = NULL;
char *sounds_set = NULL; char *sounds_set = NULL;
char *music_set = NULL;
Dimension resolution = {0, 0}; Dimension resolution = {0, 0};
Year startyear = INVALID_YEAR; Year startyear = INVALID_YEAR;
uint generation_seed = GENERATE_NEW_SEED; uint generation_seed = GENERATE_NEW_SEED;
@ -446,6 +451,7 @@ int ttd_main(int argc, char *argv[])
switch (i) { switch (i) {
case 'I': free(graphics_set); graphics_set = strdup(mgo.opt); break; case 'I': free(graphics_set); graphics_set = strdup(mgo.opt); break;
case 'S': free(sounds_set); sounds_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 'm': free(musicdriver); musicdriver = strdup(mgo.opt); break;
case 's': free(sounddriver); sounddriver = strdup(mgo.opt); break; case 's': free(sounddriver); sounddriver = strdup(mgo.opt); break;
case 'v': free(videodriver); videodriver = 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]); DeterminePaths(argv[0]);
BaseGraphics::FindSets(); BaseGraphics::FindSets();
BaseSounds::FindSets(); BaseSounds::FindSets();
BaseMusic::FindSets();
ShowHelp(); ShowHelp();
return 0; return 0;
} }
@ -549,6 +556,7 @@ int ttd_main(int argc, char *argv[])
DeterminePaths(argv[0]); DeterminePaths(argv[0]);
BaseGraphics::FindSets(); BaseGraphics::FindSets();
BaseSounds::FindSets(); BaseSounds::FindSets();
BaseMusic::FindSets();
#if defined(UNIX) && !defined(__MORPHOS__) #if defined(UNIX) && !defined(__MORPHOS__)
/* We must fork here, or we'll end up without some resources we need (like sockets) */ /* 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); 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 */ /* Initialize game palette */
GfxInitPalettes(); GfxInitPalettes();
@ -748,6 +764,7 @@ int ttd_main(int argc, char *argv[])
free(const_cast<char *>(BaseGraphics::ini_set)); free(const_cast<char *>(BaseGraphics::ini_set));
free(const_cast<char *>(BaseSounds::ini_set)); free(const_cast<char *>(BaseSounds::ini_set));
free(const_cast<char *>(BaseMusic::ini_set));
free(_ini_musicdriver); free(_ini_musicdriver);
free(_ini_sounddriver); free(_ini_sounddriver);
free(_ini_videodriver); free(_ini_videodriver);

@ -104,21 +104,24 @@ static int GetCurRes()
/** Widgets of the game options menu */ /** Widgets of the game options menu */
enum GameOptionsWidgets { enum GameOptionsWidgets {
GOW_BACKGROUND, ///< Background of the window GOW_BACKGROUND, ///< Background of the window
GOW_CURRENCY_DROPDOWN, ///< Currency dropdown GOW_CURRENCY_DROPDOWN, ///< Currency dropdown
GOW_DISTANCE_DROPDOWN, ///< Measuring unit dropdown GOW_DISTANCE_DROPDOWN, ///< Measuring unit dropdown
GOW_ROADSIDE_DROPDOWN, ///< Dropdown to select the road side (to set the right side ;)) GOW_ROADSIDE_DROPDOWN, ///< Dropdown to select the road side (to set the right side ;))
GOW_TOWNNAME_DROPDOWN, ///< Town name dropdown GOW_TOWNNAME_DROPDOWN, ///< Town name dropdown
GOW_AUTOSAVE_DROPDOWN, ///< Dropdown to say how often to autosave GOW_AUTOSAVE_DROPDOWN, ///< Dropdown to say how often to autosave
GOW_LANG_DROPDOWN, ///< Language dropdown GOW_LANG_DROPDOWN, ///< Language dropdown
GOW_RESOLUTION_DROPDOWN, ///< Dropdown for the resolution GOW_RESOLUTION_DROPDOWN, ///< Dropdown for the resolution
GOW_FULLSCREEN_BUTTON, ///< Toggle fullscreen GOW_FULLSCREEN_BUTTON, ///< Toggle fullscreen
GOW_SCREENSHOT_DROPDOWN, ///< Select the screenshot type... please use PNG! GOW_SCREENSHOT_DROPDOWN, ///< Select the screenshot type... please use PNG!
GOW_BASE_GRF_DROPDOWN, ///< Use to select a base GRF GOW_BASE_GRF_DROPDOWN, ///< Use to select a base GRF
GOW_BASE_GRF_STATUS, ///< Info about missing files etc. GOW_BASE_GRF_STATUS, ///< Info about missing files etc.
GOW_BASE_GRF_DESCRIPTION,///< Description of selected base GRF GOW_BASE_GRF_DESCRIPTION, ///< Description of selected base GRF
GOW_BASE_SFX_DROPDOWN, ///< Use to select a base SFX GOW_BASE_SFX_DROPDOWN, ///< Use to select a base SFX
GOW_BASE_SFX_DESCRIPTION,///< Description of selected 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_DROPDOWN: SetDParamStr(0, BaseGraphics::GetUsedSet()->name); break;
case GOW_BASE_GRF_STATUS: SetDParam(0, BaseGraphics::GetUsedSet()->GetNumInvalid()); 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_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())); SetDParamStr(0, BaseSounds::GetUsedSet()->GetDescription(GetCurrentLanguageIsoCode()));
DrawStringMultiLine(r.left, r.right, r.top, UINT16_MAX, STR_BLACK_RAW_STRING); DrawStringMultiLine(r.left, r.right, r.top, UINT16_MAX, STR_BLACK_RAW_STRING);
break; 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)); size->height = max(size->height, (uint)GetStringHeight(STR_BLACK_RAW_STRING, size->width));
} }
break; 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: case GOW_BASE_SFX_DROPDOWN:
ShowSetMenu<BaseSounds>(this, GOW_BASE_SFX_DROPDOWN); ShowSetMenu<BaseSounds>(this, GOW_BASE_SFX_DROPDOWN);
break; 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: case GOW_BASE_SFX_DROPDOWN:
this->SetMediaSet<BaseSounds>(index); this->SetMediaSet<BaseSounds>(index);
break; 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; 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); 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(), EndContainer(),
NWidget(WWT_FRAME, COLOUR_GREY), SetDataTip(STR_GAME_OPTIONS_BASE_GRF, STR_NULL), SetPadding(0, 10, 0, 10), 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_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), NWidget(WWT_TEXT, COLOUR_GREY, GOW_BASE_GRF_STATUS), SetMinimalSize(150, 12), SetDataTip(STR_EMPTY, STR_NULL), SetFill(1, 0),
EndContainer(), EndContainer(),
@ -473,6 +513,14 @@ static const NWidgetPart _nested_game_options_widgets[] = {
EndContainer(), 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), 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(), 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(), EndContainer(),
}; };

@ -282,8 +282,8 @@ INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia<SoundsSet>, SoundsSet)
static const char * const _sound_file_names[] = { "samples" }; static const char * const _sound_file_names[] = { "samples" };
template <class T, size_t Tnum_files> template <class T, size_t Tnum_files, Subdirectory Tsubdir>
/* static */ const char * const *BaseSet<T, Tnum_files>::file_names = _sound_file_names; /* static */ const char * const *BaseSet<T, Tnum_files, Tsubdir>::file_names = _sound_file_names;
template <class Tbase_set> template <class Tbase_set>
/* static */ const char *BaseMedia<Tbase_set>::GetExtension() /* static */ const char *BaseMedia<Tbase_set>::GetExtension()

@ -249,6 +249,7 @@ static const SettingDescGlobVarList _misc_settings[] = {
SDTG_BOOL("fullscreen", S, 0, _fullscreen, false, STR_NULL, NULL), 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("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("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("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("musicdriver", SLE_STRQ, S, 0, _ini_musicdriver, NULL, STR_NULL, NULL),
SDTG_STR("sounddriver", SLE_STRQ, S, 0, _ini_sounddriver, NULL, STR_NULL, NULL), SDTG_STR("sounddriver", SLE_STRQ, S, 0, _ini_sounddriver, NULL, STR_NULL, NULL),

Loading…
Cancel
Save