diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d77071e669..ca151022ba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -107,7 +107,7 @@ git clone https://github.com/OpenTTD/OpenTTD-git-hooks.git openttd_hooks cd openttd git remote add upstream https://github.com/OpenTTD/OpenTTD.git cd .git/hooks -ln -s -t . ../../../openttd_hooks/hooks/* +ln -s ../../../openttd_hooks/hooks/* . ``` 2. If you cloned a while ago, get the latest changes from upstream: diff --git a/README.md b/README.md index 07022325de..7f5d244f58 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,10 @@ The easiest way to contact the OpenTTD team is by submitting bug reports or posting comments in our forums. You can also chat with us on IRC (#openttd on irc.oftc.net). -The OpenTTD homepage is [http://www.openttd.org/](http://www.openttd.org/). +The OpenTTD homepage is https://www.openttd.org. -You can also find the OpenTTD forums at [http://forum.openttd.org/](http://forum.openttd.org/). +You can also find the OpenTTD forums at +[https://www.tt-forums.net](https://www.tt-forums.net/viewforum.php?f=55). ### 2.1) Reporting bugs @@ -53,11 +54,11 @@ through the file called 'known-bugs.txt' which is distributed with OpenTTD like this readme. For tracking our bugs we are using GitHub's issue tracker. You can find -the tracker at [https://github.com/OpenTTD/OpenTTD/issues](https://github.com/OpenTTD/OpenTTD/issues). Before actually reporting take a look -through the already reported bugs there to see if the bug is already known. -The 'known-bugs.txt' file might be a bit outdated at the moment you are -reading it as only bugs known before the release are documented there. Also -look through the recently closed bugs. +the tracker at https://github.com/OpenTTD/OpenTTD/issues. Before actually +reporting take a look through the already reported bugs there to see if +the bug is already known. The 'known-bugs.txt' file might be a bit outdated +at the moment you are reading it as only bugs known before the release +are documented there. Also look through the recently closed bugs. When you are sure it is not already reported you should: @@ -80,8 +81,8 @@ following information in your bug report: - Bug details, including instructions how to reproduce it - Platform (Windows, Linux, FreeBSD, …) and compiler (including version) if you compiled OpenTTD yourself. -- The processor architecture of your OS (32 bits Windows, 64 bits Windows, - Linux on an ARM, Mac OS X on a PowerPC, …) +- The processor architecture of your OS (32-bit Windows, 64-bit Windows, + Linux on an ARM, Mac OS X on a PowerPC, etc.) - Attach a saved game **and** a screenshot if possible - If this bug only occurred recently please note the last version without the bug and the first version including the bug. That way we can fix it @@ -154,7 +155,7 @@ platforms are: - DOS (Allegro) - FreeBSD (SDL) - Linux (SDL or Allegro) -- MacOS X (universal) (Cocoa video and sound drivers) +- macOS (universal) (Cocoa video and sound drivers) - MorphOS (SDL) - OpenBSD (SDL) - OS/2 (SDL) @@ -210,9 +211,9 @@ when using other versions of the game. The free data files, split into OpenGFX for graphics, OpenSFX for sounds and OpenMSX for music can be found at: -- [http://www.openttd.org/download-opengfx](http://www.openttd.org/download-opengfx) for OpenGFX -- [http://www.openttd.org/download-opensfx](http://www.openttd.org/download-opensfx) for OpenSFX -- [http://www.openttd.org/download-openmsx](http://www.openttd.org/download-openmsx) for OpenMSX +- https://www.openttd.org/download-opengfx for OpenGFX +- https://www.openttd.org/download-opensfx for OpenSFX +- https://www.openttd.org/download-openmsx for OpenMSX Please follow the readme of these packages about the installation procedure. The Windows installer can optionally download and install these packages. @@ -224,7 +225,6 @@ have to copy the data files from the CD-ROM into the baseset/ directory. It does not matter whether you copy them from the DOS or Windows version of Transport Tycoon Deluxe. The Windows install can optionally copy these files. You need to copy the following files: - - sample.cat - trg1r.grf or TRG1.GRF - trgcr.grf or TRGC.GRF @@ -234,11 +234,11 @@ You need to copy the following files: #### 4.1.3) Original Transport Tycoon Deluxe music -If you want the Transport Tycoon Deluxe music, copy the files from the gm/ -folder from the Windows version of Transport Tycoon Deluxe to the baseset -folder in your OpenTTD folder (also explained in the following sections). -The music from the DOS version as well as the original Transport Tycoon does -not work. +If you want the Transport Tycoon Deluxe music, copy the appropriate files from +the original game into the baseset folder. +- TTD for Windows: All files in the gm/ folder (gm_tt00.gm up to gm_tt21.gm) +- TTD for DOS: The GM.CAT file +- Transport Tycoon Original: The GM.CAT file, but rename it to GM-TTO.CAT #### 4.1.4) AIs @@ -247,10 +247,9 @@ not possible or you want to use an AI that has not been uploaded to the content download system download the tar file and place it in the ai/ directory. If the AI needs libraries you will have to download those too and put them in the ai/library/ directory. All AIs and AI Libraries that have been uploaded to -the content download system can be found at http://noai.openttd.org/downloads/ +the content download system can be found at https://noai.openttd.org/downloads. The AIs and libraries can be found their in the form of .tar.gz packages. -OpenTTD can read inside tar files but it does not extract .tar.gz files by -itself. +OpenTTD can read inside tar files but it does not extract .tar.gz files by itself. To figure out which libraries you need for an AI you have to start the AI and wait for an error message to pop up. The error message will tell you @@ -279,48 +278,49 @@ your operating system: 1. The current working directory (from where you started OpenTTD) - For non-Windows operating systems OpenTTD will not scan for files in this - directory if it is your personal directory, i.e. '~/', or when it is the - root directory, i.e. '/'. + For non-Windows operating systems OpenTTD will not scan for files in this + directory if it is your personal directory, i.e. '~/', or when it is the + root directory, i.e. '/'. + 2. Your personal directory - Windows: - `C:\My Documents\OpenTTD` (95, 98, ME) - `C:\Documents and Settings\\My Documents\OpenTTD` (2000, XP) - - `C:\Users\\Documents\OpenTTD` (Vista, 7) - - Mac OSX: `~/Documents/OpenTTD` + - `C:\Users\\Documents\OpenTTD` (Vista, 7, 8.1, 10) + - macOS: `~/Documents/OpenTTD` - Linux: `$XDG_DATA_HOME/openttd` which is usually `~/.local/share/openttd` - when built with XDG base directory support, otherwise `~/.openttd` + when built with XDG base directory support, otherwise `~/.openttd` 3. The shared directory - Windows: - `C:\Documents and Settings\All Users\Shared Documents\OpenTTD` (2000, XP) - - `C:\Users\Public\Documents\OpenTTD` (Vista, 7) - - Mac OSX: `/Library/Application Support/OpenTTD` + - `C:\Users\Public\Documents\OpenTTD` (Vista, 7, 8.1, 10) + - macOS: `/Library/Application Support/OpenTTD` - Linux: not available 4. The binary directory (where the OpenTTD executable is) - Windows: `C:\Program Files\OpenTTD` - Linux: `/usr/games` 5. The installation directory (Linux only) - Linux: `/usr/share/games/openttd` -6. The application bundle (Mac OSX only) +6. The application bundle (macOS only) - It includes the OpenTTD files (grf+lng) and it will work as long as they - are not touched + It includes the OpenTTD files (grf+lng) and it will work as long as they + are not touched Different types of data or extensions go into different subdirectories of the chosen main OpenTTD directory: -| data type | directory | additional info | +| data type | directory | additional info | | --- | --- | --- | | Config File | (no subdirectory) | | | Screenshots | screenshot | | -| Base Graphics | baseset| (or a subdirectory thereof) | -| Sound Sets | baseset| (or a subdirectory thereof) | -| NewGRFs | newgrf| (or a subdirectory thereof) | -| 32bpp Sets | newgrf| (or a subdirectory thereof) | -| Music Sets | baseset| (or a subdirectory thereof) | -| AIs | ai| (or a subdirectory thereof) | -| AI Libraries | ai/library| (or a subdirectory thereof) | -| Game Scripts (GS) | game| (or a subdirectory thereof) | +| Base Graphics | baseset | (or a subdirectory thereof) | +| Sound Sets | baseset | (or a subdirectory thereof) | +| NewGRFs | newgrf | (or a subdirectory thereof) | +| 32bpp Sets | newgrf | (or a subdirectory thereof) | +| Music Sets | baseset | (or a subdirectory thereof) | +| AIs | ai | (or a subdirectory thereof) | +| AI Libraries | ai/library | (or a subdirectory thereof) | +| Game Scripts (GS) | game | (or a subdirectory thereof) | | GS Libraries | game/library | (or a subdirectory thereof) | | Savegames | save | | | Automatic Savegames | save/autosave | | @@ -332,16 +332,16 @@ use and no files should be added to it or its subdirectories manually. #### Notes: - Linux in the previous list means .deb, but most paths should be similar for - others. + others. - The previous search order is also used for NewGRFs and openttd.cfg. - If openttd.cfg is not found, then it will be created using the 2, 4, 1, 3, - 5 order. When built with XDG base directory support, openttd.cfg will be - created in $XDG_CONFIG_HOME/openttd which is usually ~/.config/openttd. + 5 order. When built with XDG base directory support, openttd.cfg will be + created in $XDG_CONFIG_HOME/openttd which is usually ~/.config/openttd. - Savegames will be relative to the config file only if there is no save/ - directory in paths with higher priority than the config file path, but - autosaves and screenshots will always be relative to the config file. - Unless the configuration file is in $XDG_CONFIG_HOME/openttd, then all - other files will be saved under $XDG_DATA_HOME/openttd. + directory in paths with higher priority than the config file path, but + autosaves and screenshots will always be relative to the config file. + Unless the configuration file is in $XDG_CONFIG_HOME/openttd, then all + other files will be saved under $XDG_DATA_HOME/openttd. #### The preferred setup: @@ -391,19 +391,18 @@ OpenTTD has a lot of features going beyond the original Transport Tycoon Deluxe emulation. Unfortunately, there is currently no comprehensive list of features, but there is a basic features list on the web, and some optional features can be controlled through the Advanced Settings dialog. We also implement some -features known from [TTDPatch](http://www.ttdpatch.net/). +features known from [TTDPatch](https://www.ttdpatch.net). Several important non-standard controls: - Ctrl modifies many commands and makes them more powerful. For example Ctrl - clicking on signals with the build signal tool changes their behaviour, holding - Ctrl while the track build tool is activated changes it to the track removal - tool, and so on. See [http://wiki.openttd.org/Hidden_features](http://wiki.openttd.org/Hidden_features) - for a non-comprehensive list or look at the tooltips. -- Ingame console. More information at - [http://wiki.openttd.org/index.php/Console](http://wiki.openttd.org/index.php/Console) + clicking on signals with the build signal tool changes their behaviour, + holding Ctrl while the track build tool is activated changes it to the track + removal tool, and so on. See https://wiki.openttd.org/Hidden_features + for a non-comprehensive list or look at the tooltips. +- Ingame console. More information at https://wiki.openttd.org/Console - Hovering over a GUI element shows tooltips. This can be changed to right click - via the advanced settings. + via the advanced settings. ### 5.1) Logging of potentially dangerous actions @@ -424,7 +423,7 @@ Information logged: - Changing NewGRF parameters, loading compatible NewGRF - Changing game mode (scenario editor <-> normal game) - Loading game saved in a different OpenTTD / TTDPatch / Transport Tycoon Deluxe / - original Transport Tycoon version + original Transport Tycoon version - Running a modified OpenTTD build - Changing settings affecting NewGRF behaviour (non-network-safe settings) - Triggering NewGRF bugs @@ -453,7 +452,7 @@ you need to add WITH_SDL to the project settings. PNG (WITH_PNG) and ZLIB (WITH_ZLIB) support is enabled by default. For these to work you need their development files. For best results, download the -openttd-useful.zip file from [http://www.openttd.org/download-openttd-useful](http://www.openttd.org/download-openttd-useful) +openttd-useful.zip file from https://www.openttd.org/download-openttd-useful. Put the header files into your compiler's include/ directory and the library (.lib) files into the lib/ directory. For more help with VS see docs/Readme_Windows_MSVC.txt. @@ -470,7 +469,7 @@ Use '`gmake`', but do a '`./configure`' before the first build. OpenTTD can be built with GNU '`make`'. On non-GNU systems it is called '`gmake`'. However, for the first build one has to do a '`./configure`' first. -### MacOS X: +### macOS: Use '`make`' or Xcode (which will then call make for you) This will give you a binary for your CPU type (PPC/Intel) @@ -511,19 +510,19 @@ The following libraries are used by OpenTTD for: - libSDL/liballegro: hardware access (video, sound, mouse) - zlib: (de)compressing of old (0.3.0-1.0.5) savegames, content downloads, - heightmaps + heightmaps - liblzo2: (de)compressing of old (pre 0.3.0) savegames - liblzma: (de)compressing of savegames (1.1.0 and later) - libpng: making screenshots and loading heightmaps - libfreetype: loading generic fonts and rendering them - libfontconfig: searching for fonts, resolving font names to actual fonts - libicu: handling of right-to-left scripts (e.g. Arabic and Persian) and - natural sorting of strings. + natural sorting of strings. OpenTTD does not require any of the libraries to be present, but without liblzma you cannot open most recent savegames and without zlib you cannot open most older savegames or use the content downloading system. -Without libSDL/liballegro on non-Windows and non-MacOS X machines you have +Without libSDL/liballegro on non-Windows and non-macOS machines you have no graphical user interface; you would be building a dedicated server. ### 7.2) Supported compilers @@ -531,25 +530,25 @@ no graphical user interface; you would be building a dedicated server. The following compilers are known to compile OpenTTD: - Microsoft Visual C++ (MSVC) 2005, 2008 and 2010. - Version 2005 gives bogus warnings about scoping issues. + Version 2005 gives bogus warnings about scoping issues. - GNU Compiler Collection (GCC) 3.3 - 4.4, 4.6 - 4.8. - Versions 4.1 and earlier give bogus warnings about uninitialised variables. - Versions 4.4, 4.6 give bogus warnings about freeing non-heap objects. - Versions 4.6 and later give invalid warnings when lto is enabled. - Intel C++ Compiler (ICC) 12.0. - Clang/LLVM 2.9 - 3.0 - Version 2.9 gives bogus warnings about code nonconformity. + Version 2.9 gives bogus warnings about code nonconformity. The following compilers are known not to compile OpenTTD: - Microsoft Visual C++ (MSVC) 2003 and earlier. - GNU Compiler Collection (GCC) 3.2 and earlier. - These old versions fail due to OpenTTD's template usage. + These old versions fail due to OpenTTD's template usage. - GNU Compiler Collection (GCC) 4.5. It optimizes enums too aggressively. - See http://bugs.openttd.org/task/5513 and references therein. + See https://github.com/OpenTTD/OpenTTD/issues/5513 and references therein. - Intel C++ Compiler (ICC) 11.1 and earlier. - Version 10.0 and earlier fail a configure check and fail with recent - system headers. + system headers. - Version 10.1 fails to compile station_gui.cpp. - Version 11.1 fails with an internal error when compiling network.cpp. - Clang/LLVM 2.8 and earlier. @@ -562,7 +561,7 @@ Patches to support more compilers are welcome. To recompile the extra graphics needed to play with the original Transport Tycoon Deluxe graphics you need GRFCodec (which includes NFORenum) as well. -GRFCodec can be found at: [http://www.openttd.org/download-grfcodec](http://www.openttd.org/download-grfcodec) +GRFCodec can be found at https://www.openttd.org/download-grfcodec. The compilation of these extra graphics does generally not happen, unless you remove the graphics file using '`make maintainer-clean`'. @@ -575,17 +574,16 @@ modification of the base set files by the build process. ## 8.0) Translating -See [http://www.openttd.org/development](http://www.openttd.org/development) for up-to-date information. +See https://www.openttd.org/development for up-to-date information. The use of the online Translator service, located at -[http://translator.openttd.org/](http://translator.openttd.org/), is highly -encouraged. For getting an account simply follow the guidelines in the FAQ of -the translator website. +https://translator.openttd.org, is highly encouraged. For getting an account +simply follow the guidelines in the FAQ of the translator website. If for some reason the website is down for a longer period of time, the information below might be of help. -Please contact the translations manager ([http://www.openttd.org/contact](http://www.openttd.org/contact)) +Please contact the translations manager (https://www.openttd.org/contact) before beginning the translation process! This avoids double work, as someone else may have already started translating to the same language. @@ -604,8 +602,8 @@ Note: Do not alter the following parts of the file: - String identifiers (the first word on each line) - Parts of the strings which are in curly braces (such as {STRING}) -- Lines beginning with ## (such as ##id), other than the first two lines of - the file +- Lines beginning with ## (such as ##id), other than the first two lines + of the file ### 8.2) Previewing @@ -637,7 +635,7 @@ If the game is acting strange and you feel adventurous you can try the debugging output. The 'name' variable can help you to display only some type of debugging messages. This is mostly undocumented so best is to look in the source code file debug.c for the various debugging types. For more information -look at [http://wiki.openttd.org/index.php/Command_line](http://wiki.openttd.org/index.php/Command_line). +look at https://wiki.openttd.org/Command_line. The most frequent problem is missing data files. Please install OpenGFX and possibly OpenSFX and OpenMSX. See section 4.1.1 for more information. @@ -661,16 +659,16 @@ and add a suitable font for the small, medium and / or large font, e.g.: You should use a font name like 'Tahoma' or a path to the desired font. -Any NewGRF file used in a game is stored inside the savegame and will refuse -to load if you do not have that NewGRF file available. A list of missing files -can be viewed in the NewGRF window accessible from the file load dialogue window. +Any NewGRF file used in a game is stored inside the savegame and will refuse to +load if you do not have that NewGRF file available. A list of missing files can +be viewed in the NewGRF window accessible from the file load dialogue window. You can try to obtain the missing files from that NewGRF dialogue or – if they -are not available online – you can search manually through our [forum's graphics -development section](http://www.tt-forums.net/viewforum.php?f=66) or GrfCrawler -(http://grfcrawler.tt-forums.net/). Put the NewGRF files in OpenTTD's newgrf folder -(see section 4.2 'OpenTTD directories') and rescan the list of available NewGRFs. -Once you have all missing files, you are set to go. +are not available online – you can search manually through our +[forum's graphics development section](https://www.tt-forums.net/viewforum.php?f=66) +or [GRFCrawler](https://grfcrawler.tt-forums.net). Put the NewGRF files in +OpenTTD's newgrf folder (see section 4.2 'OpenTTD directories') and rescan the +list of available NewGRFs. Once you have all missing files, you are set to go. ## 10.0) Licensing @@ -699,8 +697,14 @@ os/dos/exe2coff/copying.dj for the exact licensing terms. The CWSDPMI implementation in os/dos/cwsdpmi is distributed under a custom binary-only license that prohibits modification. The exact licensing terms can be found in os/dos/cwsdpmi/cwsdpmi.txt. The sources -for these files can be downloaded at its author site, at: -[http://homer.rice.edu/~sandmann/cwsdpmi/csdpmi5s.zip](http://homer.rice.edu/~sandmann/cwsdpmi/csdpmi5s.zip) +for these files can be downloaded at its author site, at +http://homer.rice.edu/~sandmann/cwsdpmi/csdpmi5s.zip. + +CONTRIBUTING.md is adapted from +[Bootstrap](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md) +under the [Creative Commons Attribution 3.0 Unported +License](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE) +terms for Bootstrap documentation. ## X.X) Credits @@ -723,7 +727,7 @@ for these files can be downloaded at its author site, at: ### Inactive Developers: - Jean-François Claeys (Belugas) - GUI, newindustries and more (0.4.5 - 1.0) -- Bjarni Corfitzen (Bjarni) - MacOSX port, coder and vehicles (0.3 - 0.7) +- Bjarni Corfitzen (Bjarni) - macOS port, coder and vehicles (0.3 - 0.7) - Victor Fischer (Celestar) - Programming everywhere you need him to (0.3 - 0.6) - Jaroslav Mazanec (KUDr) - YAPG (Yet Another Pathfinder God) ;) (0.4.5 - 0.6) - Jonathan Coome (Maedhros) - High priest of the NewGRF Temple (0.5 - 0.6) @@ -737,10 +741,10 @@ for these files can be downloaded at its author site, at: - Tamás Faragó (Darkvater) - Ex-Lead coder (0.3 - 0.5) - Dominik Scherer (dominik81) - Lead programmer, GUI expert (0.3 - 0.3) -- Emil Djupfeld (egladil) - MacOSX port (0.4 - 0.6) +- Emil Djupfeld (egladil) - macOS port (0.4 - 0.6) - Simon Sasburg (HackyKid) - Bug fixer (0.4 - 0.4.5) - Ludvig Strigeus (ludde) - Original author of OpenTTD, main coder (0.1 - 0.3) -- Cian Duffy (MYOB) - BeOS port / manual writing (0.1 - 0.3) +- Cian Duffy (MYOB) - BeOS port / manual writing (0.1 - 0.3) - Petr Baudiš (pasky) - Many patches, newgrf support, etc. (0.3 - 0.3) - Benedikt Brüggemeier (skidd13) - Bug fixer and code reworker (0.6 - 0.7) - Serge Paquet (vurlix) - 2nd contributor after ludde (0.1 - 0.3) @@ -762,4 +766,3 @@ for these files can be downloaded at its author site, at: - All Translators - For their support to make OpenTTD a truly international game - Bug Reporters - Thanks for all bug reports - Chris Sawyer - For an amazing game! - diff --git a/bin/baseset/orig_dos.obm b/bin/baseset/orig_dos.obm new file mode 100644 index 0000000000..cdacc8e7f5 --- /dev/null +++ b/bin/baseset/orig_dos.obm @@ -0,0 +1,76 @@ +; $Id$ +; +; This represents the original music as on the Transport +; Tycoon Deluxe for DOS CD. +; +[metadata] +name = original_dos +shortname = TTDD +version = 1 +description = Original Transport Tycoon Deluxe DOS edition music. + +[files] +theme = gm.cat +old_0 = gm.cat +old_1 = gm.cat +old_2 = gm.cat +old_3 = gm.cat +old_4 = gm.cat +old_5 = gm.cat +old_6 = gm.cat +old_7 = gm.cat +old_8 = +old_9 = +new_0 = gm.cat +new_1 = gm.cat +new_2 = gm.cat +new_3 = gm.cat +new_4 = gm.cat +new_5 = gm.cat +new_6 = gm.cat +new_7 = +new_8 = +new_9 = +ezy_0 = gm.cat +ezy_1 = gm.cat +ezy_2 = gm.cat +ezy_3 = gm.cat +ezy_4 = gm.cat +ezy_5 = gm.cat +ezy_6 = +ezy_7 = +ezy_8 = +ezy_9 = + +[md5s] +gm.cat = 7a29d2d0c4f7d2e03091ffa9b2bdfffb + +[catindex] +theme = 0 +old_0 = 1 +old_1 = 8 +old_2 = 2 +old_3 = 9 +old_4 = 14 +old_5 = 15 +old_6 = 19 +old_7 = 13 +new_0 = 6 +new_1 = 11 +new_2 = 10 +new_3 = 17 +new_4 = 21 +new_5 = 18 +new_6 = 5 +ezy_0 = 12 +ezy_1 = 7 +ezy_2 = 16 +ezy_3 = 3 +ezy_4 = 20 +ezy_5 = 4 + +[names] +; Names get read from the CAT file + +[origin] +default = You can find it on your Transport Tycoon Deluxe CD-ROM. diff --git a/bin/baseset/orig_tto.obm b/bin/baseset/orig_tto.obm new file mode 100644 index 0000000000..13b3efb110 --- /dev/null +++ b/bin/baseset/orig_tto.obm @@ -0,0 +1,71 @@ +; $Id$ +; +; This represents the original music as on the Transport +; Tycoon (with World Editor) for DOS CD. +; +[metadata] +name = original_tto +shortname = TTOD +version = 1 +description = Original Transport Tycoon (Original/World Editor) music. + +[files] +theme = gm-tto.cat +old_0 = gm-tto.cat +old_1 = gm-tto.cat +old_2 = gm-tto.cat +old_3 = gm-tto.cat +old_4 = gm-tto.cat +old_5 = gm-tto.cat +old_6 = gm-tto.cat +old_7 = gm-tto.cat +old_8 = +old_9 = +new_0 = gm-tto.cat +new_1 = gm-tto.cat +new_2 = gm-tto.cat +new_3 = gm-tto.cat +new_4 = gm-tto.cat +new_5 = gm-tto.cat +new_6 = gm-tto.cat +new_7 = gm-tto.cat +new_8 = +new_9 = +ezy_0 = +ezy_1 = +ezy_2 = +ezy_3 = +ezy_4 = +ezy_5 = +ezy_6 = +ezy_7 = +ezy_8 = +ezy_9 = + +[catindex] +theme = 0 +old_0 = 1 +old_1 = 6 +old_2 = 2 +old_3 = 7 +old_4 = 11 +old_5 = 12 +old_6 = 15 +old_7 = 10 +new_0 = 4 +new_1 = 5 +new_2 = 9 +new_3 = 8 +new_4 = 13 +new_5 = 16 +new_6 = 14 +new_7 = 3 + +[md5s] +gm-tto.cat = 26e85ff84b0063aa5da05dd4698fc76e + +[names] +; Names get read from the CAT file + +[origin] +default = You can find it on your Transport Tycoon CD-ROM. diff --git a/bin/baseset/orig_win.obm b/bin/baseset/orig_win.obm index 8e2053e043..c8c4923e0e 100644 --- a/bin/baseset/orig_win.obm +++ b/bin/baseset/orig_win.obm @@ -142,5 +142,17 @@ GM_TT19.GM = Funk Central GM_TT20.GM = Jammit GM_TT21.GM = Movin' On +; MIDI timecodes where the playback should attemp to start and stop short. +; This is to allow fixing undesired silences in original MIDI files. +; However not all music drivers may support this. +[timingtrim] +; Theme has two beats silence at the beginning which prevents clean looping. +GM_TT00.GM = 768:53760 +; Can't Get There From Here from the Windows version has a long silence at the end, +; followed by a solo repeat. This isn't in the original DOS version music and is likely +; unintentional from the people who converted the music from the DOS version. +; Actual song ends after measure 152. +GM_TT10.GM = 0:235008 + [origin] default = You can find it on your Transport Tycoon Deluxe CD-ROM. diff --git a/changelog.txt b/changelog.txt index 8edba15fb4..5e1540a1e0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,44 @@ +1.8.0 (2018-04-01) +------------------------------------------------------------------------ +(None) + + +1.8.0-RC1 (2018-03-21) +------------------------------------------------------------------------ +- Feature: [GFX] Climate-specific Action5 extra airport sprites [FS#6664] (r27976) +- Feature: Draw vertical separators at tile distance in the train depot GUI (r27938, r27899) +- Feature: [Build] MSVC 2017 project file generator. Most noticeable, std:c++latest is enabled (r27920, r27919, r27918, r27917) +- Feature: [Build] Project file generator for kdevelop 4/5 [FS#6577] (r27897) +- Feature: Add option to close windows with right click [FS#4950] (r27826, r27825) +- Feature: Vehicle Group Info: Add profits and occupancy display to group vehicle list (r27822) +- Feature: Display aircraft type in vehicle preview/purchase/detail windows (r27802, r27799, r27797) +- Change: [NewGRF] Various performance improvements to resolving VA2 (r27989, r27985, r27984, r27983, r27982) +- Change: [NewGRF] Increase maximum allowed vehicle sprite size to reduce clipping of ships (r27987) +- Change: Check companies for bankruptcy before subtracting reoccuring monthly costs [FS#6679] (r27981) +- Change: [GFX] Replace the office building sprite on various toyland airports with a better fitting sprite [FS#6664] (r27977) +- Change: [GFX] The switch-toolbar icon contained pixels from the fire cycle. Replace the whole icon with a new version [FS#6654] (r27961) +- Change: Reword texts in industry view, when stockpiling is used (r27952) +- Change: Remove the gap between windows when positioning them after opening [FS#6568] (r27934, r27900) +- Change: [Build] Enable usage of static_assert for MSVC (r27916) +- Change: [Build] Preserve PKG_CONFIG_PATH and PKG_CONFIG_LIBDIR environment variables in config.cache file [FS#6614] (r27902) +- Change: Do not cancel headquarter construction and engine-preview-query when shift-clicking (r27889) +- Change: Parse extmidi command string for parameters to pass on (r27834) +- Change: Draw images in centre of buttons (r27829, r27821) +- Fix: Store the map variety setting in the savegame like the other mapgen settings, so restarting maps considers it [FS#6673] (r27978) +- Fix: Hair selection was missing one option [FS#6642] (r27975) +- Fix: Avoid tile operations outside map border when building lock [FS#6662] (r27973) +- Fix: Catenary sprites got mixed up for depots [FS#6670] (r27972) +- Fix: Make automatic window-positioning RTL-aware (r27934, r27900) +- Fix: Automatic window-positioning now uses GUI-scale/style dependent sizes/distances instead of fixed pixel values (r27934, r27900) +- Fix: [NewGRF] While executing random triggers, var 5F should include the new triggers (r27928) +- Fix: [NewGRF] Reset used random triggers only after all A123 chains have been resolved, so that all RA2 in all chains can test the shared triggers (r27928) +- Fix: [NewGRF] Industry random triggers are stored per tile, even when randomising the shared random bits of the parent industry (r27928) +- Fix: [NPF] Reserved track bits were not accounted for when trying to find any safe position (r27912) +- Fix: Do not modify argv[0] [FS#6575] (r27886) +- Fix: Do not search directories when opening ini files as we already have their full path [FS#6421] (r27816) +- Fix: Road tunnel/bridge heads have no trackbits wrt. catenary drawing (r27812) + + 1.7.2 (2017-12-24) ------------------------------------------------------------------------ (None) diff --git a/config.lib b/config.lib index ca99200903..af1a1efe94 100644 --- a/config.lib +++ b/config.lib @@ -88,6 +88,7 @@ set_default() { with_icu_layout="1" with_icu_sort="1" static_icu="0" + with_uniscribe="1" with_threads="1" with_distcc="1" with_ccache="1" @@ -164,6 +165,7 @@ set_default() { with_icu_layout with_icu_sort static_icu + with_uniscribe with_threads with_distcc with_ccache @@ -401,6 +403,10 @@ detect_params() { --static-libicu) static_icu="1";; --static-libicu=*) static_icu="$optarg";; + --with-uniscribe) with_uniscribe="2";; + --without-uniscribe) with_uniscribe="0";; + --with-uniscribe=*) with_uniscribe="$optarg";; + --disable-builtin-depend) enable_builtin_depend="0";; --enable-builtin-depend) enable_builtin_depend="2";; --enable-builtin-depend=*) enable_builtin_depend="$optarg";; @@ -855,6 +861,28 @@ check_params() { fi fi + if [ "$with_uniscribe" != "0" ]; then + if [ "$os" != "MINGW" ]; then + if [ "$with_uniscribe" != "1" ]; then + log 1 "configure: error: Uniscribe is only supported on native Win32 targets" + exit 1 + fi + with_uniscribe="0" + + log 1 "checking Uniscribe text layout... not Windows, skipping" + else + log 1 "checking Uniscribe text layout... found" + + # Don't use ICU unless forced. + if [ "$with_icu_layout" = "1" ]; then + with_icu_layout="0" + fi + if [ "$with_icu_sort" = "1" ]; then + with_icu_sort="0" + fi + fi + fi + detect_xdg_basedir detect_png detect_freetype @@ -1795,6 +1823,10 @@ make_cflags_and_ldflags() { fi fi + if [ "$with_uniscribe" != "0" ]; then + CFLAGS="$CFLAGS -DWITH_UNISCRIBE" + LIBS="$LIBS -lusp10" + fi if [ "$with_direct_music" != "0" ]; then CFLAGS="$CFLAGS -DWIN32_ENABLE_DIRECTMUSIC_SUPPORT" @@ -2649,6 +2681,11 @@ detect_library() { eval "res=\$$2" if [ -z "$res" ]; then log 2 " trying /mingw/include/$4$5... no" + eval "$2=`ls -1 /mingw$cpu_type/include/$4*.h 2>/dev/null | egrep \"\/$5\$\"`" + fi + eval "res=\$$2" + if [ -z "$res" ]; then + log 2 " trying /mingw$cpu_type/include/$4$5... no" eval "$2=`ls -1 /opt/local/include/$4*.h 2>/dev/null | egrep \"\/$5\$\"`" fi eval "res=\$$2" @@ -2697,6 +2734,11 @@ detect_library() { eval "res=\$$2" if [ -z "$res" ]; then log 2 " trying /mingw/lib/$3... no" + eval "$2=`ls /mingw$cpu_type/lib/*.a 2>/dev/null | egrep \"\/$3\$\"`" + fi + eval "res=\$$2" + if [ -z "$res" ]; then + log 2 " trying /mingw$cpu_type/lib/$3... no" log 1 "configure: error: $2 couldn't be found" log 1 "configure: error: you requested a static link, but I can't find $3" diff --git a/docs/Readme_Windows_MSVC.txt b/docs/Readme_Windows_MSVC.txt index 65701e0e86..00896f00b9 100644 --- a/docs/Readme_Windows_MSVC.txt +++ b/docs/Readme_Windows_MSVC.txt @@ -1,27 +1,24 @@ Compiling OpenTTD using Microsoft Visual C++ -Last updated: 2010-01-03 +Last updated: 2018-03-21 -------------------------------------------- PLEASE READ THE ENTIRE DOCUMENT BEFORE DOING ANY ACTUAL CHANGES!! SUPPORTED MSVC COMPILERS ------------------------ -OpenTTD includes projects for MSVC 2005.NET and MSVC 2008.NET. Both will -compile out of the box, providing you have the required libraries/headers; -which ones, see below. There is no support for VS6 or MSVC 2002, or -MSVC 2003.NET. You are therefore strongly encouraged to either upgrade to -MSVC 2008 Express (free) or use GCC. +OpenTTD includes projects for Microsoft Visual Studio 2005 and later. +This is the earliest compiler supported, Visual C++ 2003, Visual C++ 6.0, +or earlier, will not compile OpenTTD. +You can download the free Visual Studio Community Edition from Microsoft. 1) REQUIRED FILES ----------------- You might already have some of the files already installed, so check before -downloading; mostly because the DirectX SDK and Platform SDK are about -500MB each. +downloading; mostly because the Platform SDK is about 500MB. Download the following files: * openttd-useful.zip (http://binaries.openttd.org/extra/openttd-useful/) - * DirectX 8.1 SDK (http://neuron.tuke.sk/~mizanin/eng/Dx81sdk-include-lib.rar) (or alternatively the latest DirectX SDK from Microsoft) * MS Windows Platform SDK (http://www.microsoft.com/downloads/details.aspx?FamilyId=A55B6B43-E24F-4EA3-A93E-40C0EC4F68E5&displaylang=en) * afxres.h (http://www-d0.fnal.gov/d0dist/dist/packages/d0ve/devel/windows/AFXRES.H) @@ -81,12 +78,16 @@ See section 4.1 of README.md for the required 3rdparty files and how to install 4) COMPILING ------------ -Open trunk/openttd_vs[89]0.sln +Open the appropriate "sln" (Solution) file for your version of Visual Studio: + - VS 2005: projects/openttd_vs80.sln + - VS 2008: projects/openttd_vs90.sln + - VS 2010: projects/openttd_vs100.sln + - VS 2015: projects/openttd_vs140.sln Set the build mode to 'Release' in Build > Configuration manager > Active solution configuration > select "Release" Compile... -If everything works well the binary should be in trunk/objs/Win[32|64]/Release/openttd.exe +If everything works well the binary should be in objs/Win[32|64]/Release/openttd.exe 5) EDITING, CHANGING SOURCE CODE diff --git a/docs/obm_format.txt b/docs/obm_format.txt index 40f829a522..45fe5f879e 100644 --- a/docs/obm_format.txt +++ b/docs/obm_format.txt @@ -45,12 +45,15 @@ description.en_US = howdie ; The file names are case sensitive. ; You can have empty file names; in that case no song will be loaded ; for that 'entry'. +; If you want to load music from the MPS DOS music driver "cat" format, +; specify just the name of the .cat file the song is located in, then +; fill out the "catindex" section. [files] ; The theme song for OpenTTD theme = THEME_SONG.GM ; The songs in the 'old style' category -old_0 = -old_1 = +old_0 = GM.CAT +old_1 = GM.CAT old_2 = old_3 = old_4 = @@ -86,9 +89,17 @@ ezy_9 = ; 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. +; You don't need to fill this out for "cat" format music, the song names +; are loaded directly from the file in that case. [names] THEME_SONG.GM = Tycoon DELUXE Theme +; If you are loading music from the DOS version "cat" format, specify +; which index into the file the song has. +[catindex] +old_0 = 1 +old_1 = 3 + ; 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 @@ -96,6 +107,13 @@ THEME_SONG.GM = Tycoon DELUXE Theme [md5s] THEME_SONG.GM = 45cfec1b9d8c7a0ad45e755833cbf221 +; If a song needs to have parts of the start or end cut off to avoid long +; silences, you can specify MIDI tick codes for start:end of the actual +; music part for each file here. +; Not all music drivers might support this feature. +[timingtrim] +THEME_SONG.GM = 768:53760 + ; 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 diff --git a/media/baseset/orig_dos.obm b/media/baseset/orig_dos.obm new file mode 100644 index 0000000000..9920bfdbe1 --- /dev/null +++ b/media/baseset/orig_dos.obm @@ -0,0 +1,76 @@ +; $Id$ +; +; This represents the original music as on the Transport +; Tycoon Deluxe for DOS CD. +; +[metadata] +name = original_dos +shortname = TTDD +version = 1 +!! description STR_BASEMUSIC_DOS_DESCRIPTION + +[files] +theme = gm.cat +old_0 = gm.cat +old_1 = gm.cat +old_2 = gm.cat +old_3 = gm.cat +old_4 = gm.cat +old_5 = gm.cat +old_6 = gm.cat +old_7 = gm.cat +old_8 = +old_9 = +new_0 = gm.cat +new_1 = gm.cat +new_2 = gm.cat +new_3 = gm.cat +new_4 = gm.cat +new_5 = gm.cat +new_6 = gm.cat +new_7 = +new_8 = +new_9 = +ezy_0 = gm.cat +ezy_1 = gm.cat +ezy_2 = gm.cat +ezy_3 = gm.cat +ezy_4 = gm.cat +ezy_5 = gm.cat +ezy_6 = +ezy_7 = +ezy_8 = +ezy_9 = + +[md5s] +gm.cat = 7a29d2d0c4f7d2e03091ffa9b2bdfffb + +[catindex] +theme = 0 +old_0 = 1 +old_1 = 8 +old_2 = 2 +old_3 = 9 +old_4 = 14 +old_5 = 15 +old_6 = 19 +old_7 = 13 +new_0 = 6 +new_1 = 11 +new_2 = 10 +new_3 = 17 +new_4 = 21 +new_5 = 18 +new_6 = 5 +ezy_0 = 12 +ezy_1 = 7 +ezy_2 = 16 +ezy_3 = 3 +ezy_4 = 20 +ezy_5 = 4 + +[names] +; Names get read from the CAT file + +[origin] +default = You can find it on your Transport Tycoon Deluxe CD-ROM. diff --git a/media/baseset/orig_tto.obm b/media/baseset/orig_tto.obm new file mode 100644 index 0000000000..ff600d00f6 --- /dev/null +++ b/media/baseset/orig_tto.obm @@ -0,0 +1,71 @@ +; $Id$ +; +; This represents the original music as on the Transport +; Tycoon (with World Editor) for DOS CD. +; +[metadata] +name = original_tto +shortname = TTOD +version = 1 +!! description STR_BASEMUSIC_TTO_DESCRIPTION + +[files] +theme = gm-tto.cat +old_0 = gm-tto.cat +old_1 = gm-tto.cat +old_2 = gm-tto.cat +old_3 = gm-tto.cat +old_4 = gm-tto.cat +old_5 = gm-tto.cat +old_6 = gm-tto.cat +old_7 = gm-tto.cat +old_8 = +old_9 = +new_0 = gm-tto.cat +new_1 = gm-tto.cat +new_2 = gm-tto.cat +new_3 = gm-tto.cat +new_4 = gm-tto.cat +new_5 = gm-tto.cat +new_6 = gm-tto.cat +new_7 = gm-tto.cat +new_8 = +new_9 = +ezy_0 = +ezy_1 = +ezy_2 = +ezy_3 = +ezy_4 = +ezy_5 = +ezy_6 = +ezy_7 = +ezy_8 = +ezy_9 = + +[catindex] +theme = 0 +old_0 = 1 +old_1 = 6 +old_2 = 2 +old_3 = 7 +old_4 = 11 +old_5 = 12 +old_6 = 15 +old_7 = 10 +new_0 = 4 +new_1 = 5 +new_2 = 9 +new_3 = 8 +new_4 = 13 +new_5 = 16 +new_6 = 14 +new_7 = 3 + +[md5s] +gm-tto.cat = 26e85ff84b0063aa5da05dd4698fc76e + +[names] +; Names get read from the CAT file + +[origin] +default = You can find it on your Transport Tycoon CD-ROM. diff --git a/media/baseset/orig_win.obm b/media/baseset/orig_win.obm index 8b35b6de9d..b5d4b024ed 100644 --- a/media/baseset/orig_win.obm +++ b/media/baseset/orig_win.obm @@ -90,5 +90,17 @@ GM_TT19.GM = Funk Central GM_TT20.GM = Jammit GM_TT21.GM = Movin' On +; MIDI timecodes where the playback should attemp to start and stop short. +; This is to allow fixing undesired silences in original MIDI files. +; However not all music drivers may support this. +[timingtrim] +; Theme has two beats silence at the beginning which prevents clean looping. +GM_TT00.GM = 768:53760 +; Can't Get There From Here from the Windows version has a long silence at the end, +; followed by a solo repeat. This isn't in the original DOS version music and is likely +; unintentional from the people who converted the music from the DOS version. +; Actual song ends after measure 152. +GM_TT10.GM = 0:235008 + [origin] default = You can find it on your Transport Tycoon Deluxe CD-ROM. diff --git a/os/windows/installer/install.nsi b/os/windows/installer/install.nsi index cb93d4d04d..2c4c6f94b2 100644 --- a/os/windows/installer/install.nsi +++ b/os/windows/installer/install.nsi @@ -304,6 +304,7 @@ Section /o "Copy data from Transport Tycoon Deluxe CD-ROM" Section2 ; Let's copy the files with size approximation SetOutPath "$INSTDIR\baseset" CopyFiles "$CDDRIVE\gm\*.gm" "$INSTDIR\baseset\" 1028 + CopyFiles "$CDDRIVE\gm.cat" "$INSTDIR\baseset\gm.cat" 415 CopyFiles "$CDDRIVE\sample.cat" "$INSTDIR\baseset\sample.cat" 1566 ; Copy Windows files CopyFiles "$CDDRIVE\trg1r.grf" "$INSTDIR\baseset\trg1r.grf" 2365 @@ -426,6 +427,8 @@ Section "Uninstall" Delete "$INSTDIR\baseset\trgt.grf" Delete "$INSTDIR\baseset\trgc.grf" Delete "$INSTDIR\baseset\trgi.grf" + Delete "$INSTDIR\baseset\gm.cat" + Delete "$INSTDIR\baseset\gm-tto.cat" Delete "$INSTDIR\baseset\*.gm" Delete "$INSTDIR\data\sample.cat" @@ -467,8 +470,10 @@ Section "Uninstall" ; Base sets for music Delete "$INSTDIR\gm\orig_win.obm" + Delete "$INSTDIR\gm\orig_dos.obm" Delete "$INSTDIR\gm\no_music.obm" Delete "$INSTDIR\baseset\orig_win.obm" + Delete "$INSTDIR\baseset\orig_dos.obm" Delete "$INSTDIR\baseset\no_music.obm" ; Remove remaining directories diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj index 1d038b37b2..a11947991d 100644 --- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -102,7 +102,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -131,7 +131,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -162,7 +162,7 @@ /MP %(AdditionalOptions) Disabled ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -182,7 +182,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true @@ -216,7 +216,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -245,7 +245,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -274,7 +274,7 @@ /MP %(AdditionalOptions) Disabled ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -298,7 +298,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true @@ -632,6 +632,7 @@ + @@ -1309,6 +1310,7 @@ + diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters index 7347cd74e5..02f2075a3f 100644 --- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -1053,6 +1053,9 @@ Header Files + + Header Files + Header Files @@ -3084,6 +3087,9 @@ Windows files + + Windows files + Windows files diff --git a/projects/openttd_vs100.vcxproj.in b/projects/openttd_vs100.vcxproj.in index aab1340756..b2eb33d316 100644 --- a/projects/openttd_vs100.vcxproj.in +++ b/projects/openttd_vs100.vcxproj.in @@ -102,7 +102,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -131,7 +131,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -162,7 +162,7 @@ /MP %(AdditionalOptions) Disabled ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -182,7 +182,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true @@ -216,7 +216,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -245,7 +245,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -274,7 +274,7 @@ /MP %(AdditionalOptions) Disabled ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -298,7 +298,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true diff --git a/projects/openttd_vs140.vcxproj b/projects/openttd_vs140.vcxproj index 5c33544067..7375eb0a60 100644 --- a/projects/openttd_vs140.vcxproj +++ b/projects/openttd_vs140.vcxproj @@ -105,7 +105,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -136,7 +136,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -170,7 +170,7 @@ Disabled true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -192,7 +192,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true @@ -228,7 +228,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -259,7 +259,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -291,7 +291,7 @@ Disabled true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -317,7 +317,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true @@ -653,6 +653,7 @@ + @@ -1330,6 +1331,7 @@ + diff --git a/projects/openttd_vs140.vcxproj.filters b/projects/openttd_vs140.vcxproj.filters index 7347cd74e5..02f2075a3f 100644 --- a/projects/openttd_vs140.vcxproj.filters +++ b/projects/openttd_vs140.vcxproj.filters @@ -1053,6 +1053,9 @@ Header Files + + Header Files + Header Files @@ -3084,6 +3087,9 @@ Windows files + + Windows files + Windows files diff --git a/projects/openttd_vs140.vcxproj.in b/projects/openttd_vs140.vcxproj.in index 1dd1e35219..d6ba126b5f 100644 --- a/projects/openttd_vs140.vcxproj.in +++ b/projects/openttd_vs140.vcxproj.in @@ -105,7 +105,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -136,7 +136,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -170,7 +170,7 @@ Disabled true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -192,7 +192,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true @@ -228,7 +228,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -259,7 +259,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -291,7 +291,7 @@ Disabled true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -317,7 +317,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true diff --git a/projects/openttd_vs141.vcxproj b/projects/openttd_vs141.vcxproj index 7ac5fb5590..f956e4b2d6 100644 --- a/projects/openttd_vs141.vcxproj +++ b/projects/openttd_vs141.vcxproj @@ -105,7 +105,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -136,7 +136,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -170,7 +170,7 @@ Disabled true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -192,7 +192,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true @@ -228,7 +228,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -259,7 +259,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -291,7 +291,7 @@ Disabled true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -317,7 +317,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true @@ -652,6 +652,7 @@ + @@ -1325,6 +1326,7 @@ + diff --git a/projects/openttd_vs141.vcxproj.filters b/projects/openttd_vs141.vcxproj.filters index ccced4f931..7ed9f00cef 100644 --- a/projects/openttd_vs141.vcxproj.filters +++ b/projects/openttd_vs141.vcxproj.filters @@ -1050,6 +1050,9 @@ Header Files + + Header Files + Header Files @@ -3069,6 +3072,9 @@ Windows files + + Windows files + Windows files diff --git a/projects/openttd_vs141.vcxproj.in b/projects/openttd_vs141.vcxproj.in index c470ffcdde..f231c6264d 100644 --- a/projects/openttd_vs141.vcxproj.in +++ b/projects/openttd_vs141.vcxproj.in @@ -105,7 +105,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -136,7 +136,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -170,7 +170,7 @@ Disabled true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -192,7 +192,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true @@ -228,7 +228,7 @@ Size true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;WITH_ASSERT;%(PreprocessorDefinitions) true Sync MultiThreaded @@ -259,7 +259,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true %(IgnoreSpecificDefaultLibraries) true @@ -291,7 +291,7 @@ Disabled true ..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_XAUDIO2;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR="OpenTTD";_SQ64;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -317,7 +317,7 @@ 0x0809 - winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;icuuc.lib;icuin.lib;icudt.lib;icule.lib;iculx.lib;%(AdditionalDependencies) + winmm.lib;ws2_32.lib;imm32.lib;libpng.lib;zlibstat.lib;lzo2.lib;liblzma.lib;libfreetype2.lib;%(AdditionalDependencies) true LIBCMT.lib;%(IgnoreSpecificDefaultLibraries) true diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index 1ee5d91aa2..ba1f2f1b3a 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -52,7 +52,7 @@ FavorSizeOrSpeed="2" OmitFramePointers="true" AdditionalIncludeDirectories="..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR=\"OpenTTD\";WITH_ASSERT" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR=\"OpenTTD\";WITH_ASSERT" StringPooling="true" ExceptionHandling="1" RuntimeLibrary="0" @@ -87,7 +87,7 @@ /> + + @@ -4542,6 +4546,10 @@ RelativePath=".\..\src\os\windows\ottdres.rc" > + + diff --git a/projects/openttd_vs80.vcproj.in b/projects/openttd_vs80.vcproj.in index 8b187ad857..3363d0d019 100644 --- a/projects/openttd_vs80.vcproj.in +++ b/projects/openttd_vs80.vcproj.in @@ -52,7 +52,7 @@ FavorSizeOrSpeed="2" OmitFramePointers="true" AdditionalIncludeDirectories="..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR=\"OpenTTD\";WITH_ASSERT" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR=\"OpenTTD\";WITH_ASSERT" StringPooling="true" ExceptionHandling="1" RuntimeLibrary="0" @@ -87,7 +87,7 @@ /> + + @@ -4539,6 +4543,10 @@ RelativePath=".\..\src\os\windows\ottdres.rc" > + + diff --git a/projects/openttd_vs90.vcproj.in b/projects/openttd_vs90.vcproj.in index 39be14875f..b2d3fdeb9c 100644 --- a/projects/openttd_vs90.vcproj.in +++ b/projects/openttd_vs90.vcproj.in @@ -53,7 +53,7 @@ FavorSizeOrSpeed="2" OmitFramePointers="true" AdditionalIncludeDirectories="..\objs\langs;..\objs\settings;..\src\3rdparty\squirrel\include" - PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_ICU_SORT;WITH_ICU_LAYOUT;U_STATIC_IMPLEMENTATION;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR=\"OpenTTD\";WITH_ASSERT" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_ENABLE_DIRECTMUSIC_SUPPORT;WITH_SSE;WITH_ZLIB;WITH_LZO;WITH_LZMA;LZMA_API_STATIC;WITH_PNG;WITH_FREETYPE;WITH_UNISCRIBE;ENABLE_NETWORK;WITH_PERSONAL_DIR;PERSONAL_DIR=\"OpenTTD\";WITH_ASSERT" StringPooling="true" ExceptionHandling="1" RuntimeLibrary="0" @@ -88,7 +88,7 @@ /> 0) StringCat(exptypes,SQString::Create(_ss(this), "|", -1), exptypes); found ++; diff --git a/src/3rdparty/squirrel/squirrel/squtils.h b/src/3rdparty/squirrel/squirrel/squtils.h index 28c6cbec2b..b1138dcb1a 100644 --- a/src/3rdparty/squirrel/squirrel/squtils.h +++ b/src/3rdparty/squirrel/squirrel/squtils.h @@ -90,7 +90,7 @@ public: { _vals[idx].~T(); if(idx < (_size - 1)) { - memmove(&_vals[idx], &_vals[idx+1], sizeof(T) * (_size - (size_t)idx - 1)); + memmove(static_cast(&_vals[idx]), &_vals[idx+1], sizeof(T) * (_size - (size_t)idx - 1)); } _size--; } diff --git a/src/3rdparty/squirrel/squirrel/sqvm.cpp b/src/3rdparty/squirrel/squirrel/sqvm.cpp index c66c4aca59..03ffea2303 100644 --- a/src/3rdparty/squirrel/squirrel/sqvm.cpp +++ b/src/3rdparty/squirrel/squirrel/sqvm.cpp @@ -378,8 +378,7 @@ bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQIntege } if (!tailcall) { - CallInfo lc; - memset(&lc, 0, sizeof(lc)); + CallInfo lc = {}; lc._generator = NULL; lc._etraps = 0; lc._prevstkbase = (SQInt32) ( stackbase - _stackbase ); @@ -1159,8 +1158,7 @@ bool SQVM::CallNative(SQNativeClosure *nclosure,SQInteger nargs,SQInteger stackb SQInteger oldtop = _top; SQInteger oldstackbase = _stackbase; _top = stackbase + nargs; - CallInfo lci; - memset(&lci, 0, sizeof(lci)); + CallInfo lci = {}; lci._closure = nclosure; lci._generator = NULL; lci._etraps = 0; diff --git a/src/base_media_base.h b/src/base_media_base.h index 7614118b7f..b040abcf9d 100644 --- a/src/base_media_base.h +++ b/src/base_media_base.h @@ -26,6 +26,7 @@ struct ContentInfo; struct MD5File { /** The result of a checksum check */ enum ChecksumResult { + CR_UNKNOWN, ///< The file has not been checked yet CR_MATCH, ///< The file did exist and the md5 checksum did match CR_MISMATCH, ///< The file did exist, just the md5 checksum did not match CR_NO_FILE, ///< The file did not exist @@ -34,6 +35,7 @@ struct MD5File { const char *filename; ///< filename uint8 hash[16]; ///< md5 sum of the file const char *missing_warning; ///< warning when this file is missing + ChecksumResult check_result; ///< cached result of md5 check ChecksumResult CheckMD5(Subdirectory subdir, size_t max_size) const; }; @@ -285,11 +287,32 @@ 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; +/* Functions to read DOS music CAT files, similar to but not quite the same as sound effect CAT files */ +char *GetMusicCatEntryName(const char *filename, size_t entrynum); +byte *GetMusicCatEntryData(const char *filename, size_t entrynum, size_t &entrylen); + +enum MusicTrackType { + MTT_STANDARDMIDI, ///< Standard MIDI file + MTT_MPSMIDI, ///< MPS GM driver MIDI format (contained in a CAT file) +}; + +/** Metadata about a music track. */ +struct MusicSongInfo { + char songname[32]; ///< name of song displayed in UI + byte tracknr; ///< track number of song displayed in UI + const char *filename; ///< file on disk containing song (when used in MusicSet class, this pointer is owned by MD5File object for the file) + MusicTrackType filetype; ///< decoder required for song file + int cat_index; ///< entry index in CAT file, for filetype==MTT_MPSMIDI + bool loop; ///< song should play in a tight loop if possible, never ending + int override_start; ///< MIDI ticks to skip over in beginning + int override_end; ///< MIDI tick to end the song at (0 if no override) +}; + /** All data of a music set. */ struct MusicSet : BaseSet { - /** The name of the different songs. */ - char song_name[NUM_SONGS_AVAILABLE][32]; - byte track_nr[NUM_SONGS_AVAILABLE]; + /** Data about individual songs in set. */ + MusicSongInfo songinfo[NUM_SONGS_AVAILABLE]; + /** Number of valid songs in set. */ byte num_available; bool FillSetDetails(struct IniFile *ini, const char *path, const char *full_filename); diff --git a/src/base_media_func.h b/src/base_media_func.h index f45956f765..f7afca0edb 100644 --- a/src/base_media_func.h +++ b/src/base_media_func.h @@ -40,8 +40,6 @@ template bool BaseSet::FillSetDetails(IniFile *ini, const char *path, const char *full_filename, bool allow_empty_filename) { - memset(this, 0, sizeof(*this)); - IniGroup *metadata = ini->GetGroup("metadata"); IniItem *item; @@ -129,7 +127,11 @@ bool BaseSet::FillSetDetails(IniFile *ini, const file->missing_warning = stredup(item->value); } - switch (T::CheckMD5(file, BASESET_DIR)) { + file->check_result = T::CheckMD5(file, BASESET_DIR); + switch (file->check_result) { + case MD5File::CR_UNKNOWN: + break; + case MD5File::CR_MATCH: this->valid_files++; this->found_files++; diff --git a/src/blitter/32bpp_anim.cpp b/src/blitter/32bpp_anim.cpp index 578d85f188..98ae22b00c 100644 --- a/src/blitter/32bpp_anim.cpp +++ b/src/blitter/32bpp_anim.cpp @@ -361,7 +361,7 @@ void Blitter_32bppAnim::CopyFromBuffer(void *video, const void *src, int width, Colour *dst_pal = dst; uint16 *anim_pal = anim_line; - memcpy(dst, usrc, width * sizeof(uint32)); + memcpy(static_cast(dst), usrc, width * sizeof(uint32)); usrc += width; dst += _screen.pitch; /* Copy back the anim-buffer */ diff --git a/src/cargo_type.h b/src/cargo_type.h index 402e81c1b2..c84f1f4902 100644 --- a/src/cargo_type.h +++ b/src/cargo_type.h @@ -63,14 +63,14 @@ enum CargoType { CT_PLASTIC = 10, CT_FIZZY_DRINKS = 11, - NUM_CARGO = 32, ///< Maximal number of cargo types in a game. + NUM_CARGO = 64, ///< Maximal number of cargo types in a game. CT_AUTO_REFIT = 0xFD, ///< Automatically choose cargo type when doing auto refitting. CT_NO_REFIT = 0xFE, ///< Do not refit cargo of a vehicle (used in vehicle orders and auto-replace/auto-new). CT_INVALID = 0xFF, ///< Invalid cargo type. }; -typedef uint32 CargoTypes; +typedef uint64 CargoTypes; static const CargoTypes ALL_CARGOTYPES = (CargoTypes)UINT32_MAX; diff --git a/src/company_base.h b/src/company_base.h index b8ea09f955..1bc4b7f83c 100644 --- a/src/company_base.h +++ b/src/company_base.h @@ -96,7 +96,13 @@ struct CompanyProperties { CompanyEconomyEntry old_economy[MAX_HISTORY_QUARTERS]; ///< Economic data of the company of the last #MAX_HISTORY_QUARTERS quarters. byte num_valid_stat_ent; ///< Number of valid statistical entries in #old_economy. - CompanyProperties() : name(NULL), president_name(NULL) {} + // TODO: Change some of these member variables to use relevant INVALID_xxx constants + CompanyProperties() + : name_2(0), name_1(0), name(NULL), president_name_1(0), president_name_2(0), president_name(NULL), + face(0), money(0), money_fraction(0), current_loan(0), colour(0), block_preview(0), + location_of_HQ(0), last_build_coordinate(0), share_owners(), inaugurated_year(0), + months_of_bankruptcy(0), bankrupt_asked(0), bankrupt_timeout(0), bankrupt_value(0), + terraform_limit(0), clear_limit(0), tree_limit(0), is_ai(false) {} ~CompanyProperties() { diff --git a/src/core/alloc_func.hpp b/src/core/alloc_func.hpp index c7e17421f7..c33e733016 100644 --- a/src/core/alloc_func.hpp +++ b/src/core/alloc_func.hpp @@ -125,7 +125,7 @@ static inline T *ReallocT(T *t_ptr, size_t num_elements) /* Ensure the size does not overflow. */ CheckAllocationConstraints(num_elements); - t_ptr = (T*)realloc(t_ptr, num_elements * sizeof(T)); + t_ptr = (T*)realloc(static_cast(t_ptr), num_elements * sizeof(T)); if (t_ptr == NULL) ReallocError(num_elements * sizeof(T)); return t_ptr; } diff --git a/src/economy.cpp b/src/economy.cpp index 70b0bd8376..fae71f3e42 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -696,9 +696,10 @@ static void CompaniesGenStatistics() if (!HasBit(1 << 0 | 1 << 3 | 1 << 6 | 1 << 9, _cur_month)) return; FOR_ALL_COMPANIES(c) { - memmove(&c->old_economy[1], &c->old_economy[0], sizeof(c->old_economy) - sizeof(c->old_economy[0])); + /* Drop the oldest history off the end */ + std::copy_backward(c->old_economy, c->old_economy + MAX_HISTORY_QUARTERS - 1, c->old_economy + MAX_HISTORY_QUARTERS); c->old_economy[0] = c->cur_economy; - memset(&c->cur_economy, 0, sizeof(c->cur_economy)); + c->cur_economy = {}; if (c->num_valid_stat_ent != MAX_HISTORY_QUARTERS) c->num_valid_stat_ent++; diff --git a/src/fileio.cpp b/src/fileio.cpp index 8b5e56fb5d..3ad5be711d 100644 --- a/src/fileio.cpp +++ b/src/fileio.cpp @@ -532,7 +532,7 @@ FILE *FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, * Create a directory with the given name * @param name the new name of the directory */ -static void FioCreateDirectory(const char *name) +void FioCreateDirectory(const char *name) { /* Ignore directory creation errors; they'll surface later on, and most * of the time they are 'directory already exists' errors anyhow. */ diff --git a/src/fileio_func.h b/src/fileio_func.h index 2dc9afed1d..6383180744 100644 --- a/src/fileio_func.h +++ b/src/fileio_func.h @@ -55,6 +55,7 @@ char *FioGetFullPath(char *buf, const char *last, Searchpath sp, Subdirectory su char *FioFindFullPath(char *buf, const char *last, Subdirectory subdir, const char *filename); char *FioAppendDirectory(char *buf, const char *last, Searchpath sp, Subdirectory subdir); char *FioGetDirectory(char *buf, const char *last, Subdirectory subdir); +void FioCreateDirectory(const char *name); const char *FiosGetScreenshotDir(); diff --git a/src/fontcache.cpp b/src/fontcache.cpp index 72e42ccbb3..3a5dd886c1 100644 --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -86,6 +86,7 @@ public: virtual GlyphID MapCharToGlyph(WChar key) { assert(IsPrintable(key)); return SPRITE_GLYPH | key; } virtual const void *GetFontTable(uint32 tag, size_t &length) { length = 0; return NULL; } virtual const char *GetFontName() { return "sprite"; } + virtual bool IsBuiltInFont() { return true; } }; /** @@ -208,6 +209,7 @@ bool SpriteFontCache::GetDrawGlyphShadow() class FreeTypeFontCache : public FontCache { private: FT_Face face; ///< The font face associated with this font. + int req_size; ///< Requested font size. typedef SmallMap > FontTable; ///< Table with font table cache FontTable font_tables; ///< Cached font tables. @@ -236,6 +238,7 @@ private: GlyphEntry *GetGlyphPtr(GlyphID key); void SetGlyphPtr(GlyphID key, const GlyphEntry *glyph, bool duplicate = false); + void SetFontSize(FontSize fs, FT_Face face, int pixels); public: FreeTypeFontCache(FontSize fs, FT_Face face, int pixels); @@ -250,6 +253,7 @@ public: virtual GlyphID MapCharToGlyph(WChar key); virtual const void *GetFontTable(uint32 tag, size_t &length); virtual const char *GetFontName() { return face->family_name; } + virtual bool IsBuiltInFont() { return false; } }; FT_Library _library = NULL; @@ -265,20 +269,26 @@ static const byte SHADOW_COLOUR = 2; * @param face The font that has to be loaded. * @param pixels The number of pixels this font should be high. */ -FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : FontCache(fs), face(face), glyph_to_sprite(NULL) +FreeTypeFontCache::FreeTypeFontCache(FontSize fs, FT_Face face, int pixels) : FontCache(fs), face(face), req_size(pixels), glyph_to_sprite(NULL) { assert(face != NULL); + this->SetFontSize(fs, face, pixels); +} + +void FreeTypeFontCache::SetFontSize(FontSize fs, FT_Face face, int pixels) +{ if (pixels == 0) { /* Try to determine a good height based on the minimal height recommended by the font. */ - pixels = _default_font_height[this->fs]; + int scaled_height = ScaleGUITrad(_default_font_height[this->fs]); + pixels = scaled_height; TT_Header *head = (TT_Header *)FT_Get_Sfnt_Table(this->face, ft_sfnt_head); if (head != NULL) { /* Font height is minimum height plus the difference between the default * height for this font size and the small size. */ - int diff = _default_font_height[this->fs] - _default_font_height[FS_SMALL]; - pixels = Clamp(min(head->Lowest_Rec_PPEM, 20) + diff, _default_font_height[this->fs], MAX_FONT_SIZE); + int diff = scaled_height - ScaleGUITrad(_default_font_height[FS_SMALL]); + pixels = Clamp(min(head->Lowest_Rec_PPEM, 20) + diff, scaled_height, MAX_FONT_SIZE); } } @@ -393,6 +403,7 @@ found_face: FreeTypeFontCache::~FreeTypeFontCache() { FT_Done_Face(this->face); + this->face = NULL; this->ClearFontCache(); for (FontTable::iterator iter = this->font_tables.Begin(); iter != this->font_tables.End(); iter++) { @@ -422,6 +433,9 @@ void FreeTypeFontCache::ClearFontCache() this->glyph_to_sprite = NULL; Layouter::ResetFontCache(this->fs); + + /* GUI scaling might have changed, determine font size anew if it was automatically selected. */ + if (this->face != NULL && this->req_size == 0) this->SetFontSize(this->fs, this->face, this->req_size); } FreeTypeFontCache::GlyphEntry *FreeTypeFontCache::GetGlyphPtr(GlyphID key) diff --git a/src/fontcache.h b/src/fontcache.h index 8caf4f1bd3..8d66ed3667 100644 --- a/src/fontcache.h +++ b/src/fontcache.h @@ -143,6 +143,11 @@ public: { return this->parent != NULL; } + + /** + * Is this a built-in sprite font? + */ + virtual bool IsBuiltInFont() = 0; }; /** Get the SpriteID mapped to the given font size and key */ diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp index 359709e361..04eea8e660 100644 --- a/src/genworld_gui.cpp +++ b/src/genworld_gui.cpp @@ -288,7 +288,7 @@ static DropDownList *BuildMapsizeDropDown() for (uint i = MIN_MAP_SIZE_BITS; i <= MAX_MAP_SIZE_BITS; i++) { DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_JUST_INT, i, false); - item->SetParam(0, 1 << i); + item->SetParam(0, 1LL << i); *list->Append() = item; } @@ -336,8 +336,8 @@ struct GenerateLandscapeWindow : public Window { { switch (widget) { case WID_GL_START_DATE_TEXT: SetDParam(0, ConvertYMDToDate(_settings_newgame.game_creation.starting_year, 0, 1)); break; - case WID_GL_MAPSIZE_X_PULLDOWN: SetDParam(0, 1 << _settings_newgame.game_creation.map_x); break; - case WID_GL_MAPSIZE_Y_PULLDOWN: SetDParam(0, 1 << _settings_newgame.game_creation.map_y); break; + case WID_GL_MAPSIZE_X_PULLDOWN: SetDParam(0, 1LL << _settings_newgame.game_creation.map_x); break; + case WID_GL_MAPSIZE_Y_PULLDOWN: SetDParam(0, 1LL << _settings_newgame.game_creation.map_y); break; case WID_GL_MAX_HEIGHTLEVEL_TEXT: SetDParam(0, _settings_newgame.construction.max_heightlevel); break; case WID_GL_SNOW_LEVEL_TEXT: SetDParam(0, _settings_newgame.game_creation.snow_line_height); break; @@ -896,11 +896,11 @@ struct CreateScenarioWindow : public Window break; case WID_CS_MAPSIZE_X_PULLDOWN: - SetDParam(0, 1 << _settings_newgame.game_creation.map_x); + SetDParam(0, 1LL << _settings_newgame.game_creation.map_x); break; case WID_CS_MAPSIZE_Y_PULLDOWN: - SetDParam(0, 1 << _settings_newgame.game_creation.map_y); + SetDParam(0, 1LL << _settings_newgame.game_creation.map_y); break; case WID_CS_FLAT_LAND_HEIGHT_TEXT: diff --git a/src/gfx.cpp b/src/gfx.cpp index 187d197a3d..c9c36019ce 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -1111,16 +1111,17 @@ void DoPaletteAnimations() /** * Determine a contrasty text colour for a coloured background. * @param background Background colour. + * @param threshold Background colour brightness threshold below which the background is considered dark and TC_WHITE is returned, range: 0 - 255, default 128. * @return TC_BLACK or TC_WHITE depending on what gives a better contrast. */ -TextColour GetContrastColour(uint8 background) +TextColour GetContrastColour(uint8 background, uint8 threshold) { Colour c = _cur_palette.palette[background]; /* Compute brightness according to http://www.w3.org/TR/AERT#color-contrast. * The following formula computes 1000 * brightness^2, with brightness being in range 0 to 255. */ uint sq1000_brightness = c.r * c.r * 299 + c.g * c.g * 587 + c.b * c.b * 114; - /* Compare with threshold brightness 128 (50%) */ - return sq1000_brightness < 128 * 128 * 1000 ? TC_WHITE : TC_BLACK; + /* Compare with threshold brightness which defaults to 128 (50%) */ + return sq1000_brightness < ((uint) threshold) * ((uint) threshold) * 1000 ? TC_WHITE : TC_BLACK; } /** diff --git a/src/gfx_func.h b/src/gfx_func.h index 99461f9079..9f7cb9153d 100644 --- a/src/gfx_func.h +++ b/src/gfx_func.h @@ -187,7 +187,7 @@ int GetCharacterHeight(FontSize size); extern DrawPixelInfo *_cur_dpi; -TextColour GetContrastColour(uint8 background); +TextColour GetContrastColour(uint8 background, uint8 threshold = 128); /** * All 16 colour gradients diff --git a/src/gfx_layout.cpp b/src/gfx_layout.cpp index 3290aea653..0a9f5a0b13 100644 --- a/src/gfx_layout.cpp +++ b/src/gfx_layout.cpp @@ -21,6 +21,10 @@ #include #endif /* WITH_ICU_LAYOUT */ +#ifdef WITH_UNISCRIBE +#include "os/windows/string_uniscribe.h" +#endif /* WITH_UNISCRIBE */ + #include "safeguards.h" @@ -113,26 +117,12 @@ le_bool Font::getGlyphPoint(LEGlyphID glyph, le_int32 pointNumber, LEPoint &poin return FALSE; } -static size_t AppendToBuffer(UChar *buff, const UChar *buffer_last, WChar c) -{ - /* Transform from UTF-32 to internal ICU format of UTF-16. */ - int32 length = 0; - UErrorCode err = U_ZERO_ERROR; - u_strFromUTF32(buff, buffer_last - buff, &length, (UChar32*)&c, 1, &err); - return length; -} - /** * Wrapper for doing layouts with ICU. */ class ICUParagraphLayout : public AutoDeleteSmallVector, public ParagraphLayouter { ParagraphLayout *p; ///< The actual ICU paragraph layout. public: - /** Helper for GetLayouter, to get the right type. */ - typedef UChar CharType; - /** Helper for GetLayouter, to get whether the layouter supports RTL. */ - static const bool SUPPORTS_RTL = true; - /** Visual run contains data about the bit of text with the same font. */ class ICUVisualRun : public ParagraphLayouter::VisualRun { const ParagraphLayout::VisualRun *vr; ///< The actual ICU vr. @@ -184,35 +174,54 @@ public: } }; -static ParagraphLayouter *GetParagraphLayout(UChar *buff, UChar *buff_end, FontMap &fontMapping) -{ - int32 length = buff_end - buff; +/** + * Helper class to construct a new #ICUParagraphLayout. + */ +class ICUParagraphLayoutFactory { +public: + /** Helper for GetLayouter, to get the right type. */ + typedef UChar CharType; + /** Helper for GetLayouter, to get whether the layouter supports RTL. */ + static const bool SUPPORTS_RTL = true; - if (length == 0) { - /* ICU's ParagraphLayout cannot handle empty strings, so fake one. */ - buff[0] = ' '; - length = 1; - fontMapping.End()[-1].first++; + static ParagraphLayouter *GetParagraphLayout(UChar *buff, UChar *buff_end, FontMap &fontMapping) + { + int32 length = buff_end - buff; + + if (length == 0) { + /* ICU's ParagraphLayout cannot handle empty strings, so fake one. */ + buff[0] = ' '; + length = 1; + fontMapping.End()[-1].first++; + } + + /* Fill ICU's FontRuns with the right data. */ + FontRuns runs(fontMapping.Length()); + for (FontMap::iterator iter = fontMapping.Begin(); iter != fontMapping.End(); iter++) { + runs.add(iter->second, iter->first); + } + + LEErrorCode status = LE_NO_ERROR; + /* ParagraphLayout does not copy "buff", so it must stay valid. + * "runs" is copied according to the ICU source, but the documentation does not specify anything, so this might break somewhen. */ + ParagraphLayout *p = new ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status); + if (status != LE_NO_ERROR) { + delete p; + return NULL; + } + + return new ICUParagraphLayout(p); } - /* Fill ICU's FontRuns with the right data. */ - FontRuns runs(fontMapping.Length()); - for (FontMap::iterator iter = fontMapping.Begin(); iter != fontMapping.End(); iter++) { - runs.add(iter->second, iter->first); + static size_t AppendToBuffer(UChar *buff, const UChar *buffer_last, WChar c) + { + /* Transform from UTF-32 to internal ICU format of UTF-16. */ + int32 length = 0; + UErrorCode err = U_ZERO_ERROR; + u_strFromUTF32(buff, buffer_last - buff, &length, (UChar32*)&c, 1, &err); + return length; } - - LEErrorCode status = LE_NO_ERROR; - /* ParagraphLayout does not copy "buff", so it must stay valid. - * "runs" is copied according to the ICU source, but the documentation does not specify anything, so this might break somewhen. */ - ParagraphLayout *p = new ParagraphLayout(buff, length, &runs, NULL, NULL, NULL, _current_text_dir == TD_RTL ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, false, status); - if (status != LE_NO_ERROR) { - delete p; - return NULL; - } - - return new ICUParagraphLayout(p); -} - +}; #endif /* WITH_ICU_LAYOUT */ /*** Paragraph layout ***/ @@ -236,11 +245,6 @@ static ParagraphLayouter *GetParagraphLayout(UChar *buff, UChar *buff_end, FontM */ class FallbackParagraphLayout : public ParagraphLayouter { public: - /** Helper for GetLayouter, to get the right type. */ - typedef WChar CharType; - /** Helper for GetLayouter, to get whether the layouter supports RTL. */ - static const bool SUPPORTS_RTL = false; - /** Visual run contains data about the bit of text with the same font. */ class FallbackVisualRun : public ParagraphLayouter::VisualRun { Font *font; ///< The font used to layout these. @@ -280,6 +284,42 @@ public: const ParagraphLayouter::Line *NextLine(int max_width); }; +/** + * Helper class to construct a new #FallbackParagraphLayout. + */ +class FallbackParagraphLayoutFactory { +public: + /** Helper for GetLayouter, to get the right type. */ + typedef WChar CharType; + /** Helper for GetLayouter, to get whether the layouter supports RTL. */ + static const bool SUPPORTS_RTL = false; + + /** + * Get the actual ParagraphLayout for the given buffer. + * @param buff The begin of the buffer. + * @param buff_end The location after the last element in the buffer. + * @param fontMapping THe mapping of the fonts. + * @return The ParagraphLayout instance. + */ + static ParagraphLayouter *GetParagraphLayout(WChar *buff, WChar *buff_end, FontMap &fontMapping) + { + return new FallbackParagraphLayout(buff, buff_end - buff, fontMapping); + } + + /** + * Append a wide character to the internal buffer. + * @param buff The buffer to append to. + * @param buffer_last The end of the buffer. + * @param c The character to add. + * @return The number of buffer spaces that were used. + */ + static size_t AppendToBuffer(WChar *buff, const WChar *buffer_last, WChar c) + { + *buff = c; + return 1; + } +}; + /** * Create the visual run. * @param font The font to use for this run. @@ -536,31 +576,6 @@ const ParagraphLayouter::Line *FallbackParagraphLayout::NextLine(int max_width) return l; } -/** - * Appand a wide character to the internal buffer. - * @param buff The buffer to append to. - * @param buffer_last The end of the buffer. - * @param c The character to add. - * @return The number of buffer spaces that were used. - */ -static size_t AppendToBuffer(WChar *buff, const WChar *buffer_last, WChar c) -{ - *buff = c; - return 1; -} - -/** - * Get the actual ParagraphLayout for the given buffer. - * @param buff The begin of the buffer. - * @param buff_end The location after the last element in the buffer. - * @param fontMapping THe mapping of the fonts. - * @return The ParagraphLayout instance. - */ -static FallbackParagraphLayout *GetParagraphLayout(WChar *buff, WChar *buff_end, FontMap &fontMapping) -{ - return new FallbackParagraphLayout(buff, buff_end - buff, fontMapping); -} - /** * Helper for getting a ParagraphLayouter of the given type. * @@ -582,6 +597,7 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str, Font *f = Layouter::GetFont(state.fontsize, state.cur_colour); line.buffer = buff_begin; + fontMapping.Clear(); /* * Go through the whole string while adding Font instances to the font map @@ -605,7 +621,7 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str, * will not be handled in the fallback non ICU case because they are * mostly needed for RTL languages which need more ICU support. */ if (!T::SUPPORTS_RTL && IsTextDirectionChar(c)) continue; - buff += AppendToBuffer(buff, buffer_last, c); + buff += T::AppendToBuffer(buff, buffer_last, c); continue; } @@ -621,7 +637,7 @@ static inline void GetLayouter(Layouter::LineCacheItem &line, const char *&str, if (!fontMapping.Contains(buff - buff_begin)) { fontMapping.Insert(buff - buff_begin, f); } - line.layout = GetParagraphLayout(buff_begin, buff, fontMapping); + line.layout = T::GetParagraphLayout(buff_begin, buff, fontMapping); line.state_after = state; } @@ -654,11 +670,11 @@ Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsi line.layout->Reflow(); } else { /* Line is new, layout it */ -#ifdef WITH_ICU_LAYOUT FontState old_state = state; const char *old_str = str; - GetLayouter(line, str, state); +#ifdef WITH_ICU_LAYOUT + GetLayouter(line, str, state); if (line.layout == NULL) { static bool warned = false; if (!warned) { @@ -668,11 +684,22 @@ Layouter::Layouter(const char *str, int maxw, TextColour colour, FontSize fontsi state = old_state; str = old_str; - GetLayouter(line, str, state); } -#else - GetLayouter(line, str, state); #endif + +#ifdef WITH_UNISCRIBE + if (line.layout == NULL) { + GetLayouter(line, str, state); + if (line.layout == NULL) { + state = old_state; + str = old_str; + } + } +#endif + + if (line.layout == NULL) { + GetLayouter(line, str, state); + } } /* Copy all lines into a local cache so we can reuse them later on more easily. */ @@ -809,6 +836,10 @@ void Layouter::ResetFontCache(FontSize size) /* We must reset the linecache since it references the just freed fonts */ ResetLineCache(); + +#if defined(WITH_UNISCRIBE) + UniscribeResetScriptCache(size); +#endif } /** diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp index c12c6ace4d..f4334429fb 100644 --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -33,8 +33,8 @@ #include "safeguards.h" /* Bitmasks of company and cargo indices that shouldn't be drawn. */ -static uint _legend_excluded_companies; -static uint _legend_excluded_cargo; +static CompanyMask _legend_excluded_companies; +static CargoTypes _legend_excluded_cargo; /* Apparently these don't play well with enums. */ static const OverflowSafeInt64 INVALID_DATAPOINT(INT64_MAX); // Value used for a datapoint that shouldn't be drawn. @@ -166,14 +166,14 @@ struct ValuesInterval { struct BaseGraphWindow : Window { protected: - static const int GRAPH_MAX_DATASETS = 32; + static const int GRAPH_MAX_DATASETS = 64; static const int GRAPH_AXIS_LINE_COLOUR = PC_BLACK; static const int GRAPH_NUM_MONTHS = 24; ///< Number of months displayed in the graph. static const int MIN_GRAPH_NUM_LINES_Y = 9; ///< Minimal number of horizontal lines to draw. static const int MIN_GRID_PIXEL_SIZE = 20; ///< Minimum distance between graph lines. - uint excluded_data; ///< bitmask of the datasets that shouldn't be displayed. + uint64 excluded_data; ///< bitmask of the datasets that shouldn't be displayed. byte num_dataset; byte num_on_x_axis; byte num_vert_lines; @@ -561,7 +561,7 @@ public: */ void UpdateStatistics(bool initialize) { - uint excluded_companies = _legend_excluded_companies; + CompanyMask excluded_companies = _legend_excluded_companies; /* Exclude the companies which aren't valid */ for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) { diff --git a/src/landscape.cpp b/src/landscape.cpp index 185e84a80b..a29c97b9d7 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -1080,8 +1080,7 @@ static uint River_Hash(uint tile, uint dir) */ static void BuildRiver(TileIndex begin, TileIndex end) { - AyStar finder; - MemSetT(&finder, 0); + AyStar finder = {}; finder.CalculateG = River_CalculateG; finder.CalculateH = River_CalculateH; finder.GetNeighbours = River_GetNeighbours; diff --git a/src/lang/afrikaans.txt b/src/lang/afrikaans.txt index 0e2ea20e67..bd83955062 100644 --- a/src/lang/afrikaans.txt +++ b/src/lang/afrikaans.txt @@ -2974,8 +2974,6 @@ STR_TOWN_POPULATION :{BLACK}Wêreldb STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (City) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Bevolking: {ORANGE}{COMMA}{BLACK} Huise: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passasiers verlede maand: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Pos verlede maand: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Vrag nodig om dorp te laat groei: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} vereis STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} vereis in winter diff --git a/src/lang/arabic_egypt.txt b/src/lang/arabic_egypt.txt index 748303f8db..d8bca0dbae 100644 --- a/src/lang/arabic_egypt.txt +++ b/src/lang/arabic_egypt.txt @@ -2589,8 +2589,6 @@ STR_TOWN_POPULATION :{BLACK}سكان STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} - مدينة - STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}السكان: {ORANGE}{COMMA}{BLACK} المنازل: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}الركاب الشهر الماضي: {ORANGE}{COMMA}{BLACK} الأقصى: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}طرود البريد الشهر الماضي: {ORANGE}{COMMA}{BLACK} الأقصى: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK} نمو المدينة يتطلب بضائع STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} مطلوب STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK}مطلوب في الشتاء diff --git a/src/lang/basque.txt b/src/lang/basque.txt index d40beb0a24..ddb3afb60d 100644 --- a/src/lang/basque.txt +++ b/src/lang/basque.txt @@ -2863,8 +2863,6 @@ STR_TOWN_POPULATION :{BLACK}Munduko STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Hiria) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Biztanleria: {ORANGE}{COMMA}{BLACK} Etxe kopurua: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Bidaiari kopurua aurreko hilabetean: {ORANGE}{COMMA}{BLACK} gehienez: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Posta kopurua aurreko hilabetean: {ORANGE}{COMMA}{BLACK} gehienez: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Beharrezko zama herri hazkunderako: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} beharrezkoa STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} Neguan beharrezkoa diff --git a/src/lang/belarusian.txt b/src/lang/belarusian.txt index 031ac56590..e56d431f6f 100644 --- a/src/lang/belarusian.txt +++ b/src/lang/belarusian.txt @@ -3320,8 +3320,6 @@ STR_TOWN_POPULATION :{BLACK}Насе STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Мэґаполіс) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Насельніцтва: {ORANGE}{COMMA}{BLACK} Будынкаў: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Пасажыраў за мінулы месяц: {ORANGE}{COMMA}{BLACK} Макс.: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Пошты за мінулы месяц: {ORANGE}{COMMA}{BLACK} Макс.: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Груз, неабходны для росту горада: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} патрабу{G 0 е e e ю}цца STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} патрабу{G 0 е e e ю}цца ўзімку diff --git a/src/lang/brazilian_portuguese.txt b/src/lang/brazilian_portuguese.txt index d6aa4b1287..fe033b740c 100644 --- a/src/lang/brazilian_portuguese.txt +++ b/src/lang/brazilian_portuguese.txt @@ -2978,8 +2978,6 @@ STR_TOWN_POPULATION :{BLACK}Populaç STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Cidade) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}População: {ORANGE}{COMMA}{BLACK} Casas: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passageiros no mês passado: {ORANGE}{COMMA}{BLACK} máx: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Cartas no mês passado: {ORANGE}{COMMA}{BLACK} máx: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Carga necessária para prover o crescimento: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} necessário(a) STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} necessário no inverno diff --git a/src/lang/bulgarian.txt b/src/lang/bulgarian.txt index 2a00a6a397..e06bf04da8 100644 --- a/src/lang/bulgarian.txt +++ b/src/lang/bulgarian.txt @@ -2911,8 +2911,6 @@ STR_TOWN_POPULATION :{BLACK}Обща STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Град) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Население: {ORANGE}{COMMA}{BLACK} Жилища: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Пътници през последния месец: {ORANGE}{COMMA}{BLACK} максимум: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Поща през последния месец: {ORANGE}{COMMA}{BLACK} максимум: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Товар нужен за растеж на града: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} необходим STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} необходим през зимата diff --git a/src/lang/catalan.txt b/src/lang/catalan.txt index 96573910e2..c9aba3b216 100644 --- a/src/lang/catalan.txt +++ b/src/lang/catalan.txt @@ -2978,8 +2978,6 @@ STR_TOWN_POPULATION :{BLACK}Poblaci STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Ciutat) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Població: {ORANGE}{COMMA}{BLACK} Cases: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passatgers el darrer mes: {ORANGE}{COMMA}{BLACK} màx: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Correu el darrer mes: {ORANGE}{COMMA}{BLACK} màx: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Càrrega requerida per tal que la població creixi: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} requerides STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} requerit a l'hivern diff --git a/src/lang/croatian.txt b/src/lang/croatian.txt index 9409e185ba..10f6ca94f4 100644 --- a/src/lang/croatian.txt +++ b/src/lang/croatian.txt @@ -977,10 +977,10 @@ STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLAC # Extra view window STR_EXTRA_VIEW_PORT_TITLE :{WHITE}Mini pogled {COMMA} -STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Kopiraj u mini pogled +STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Promijeni pogled STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT :{BLACK}Kopiraj lokaciju globalnog pogleda u ovaj mini pogled -STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Zalijepi iz mini pogleda -STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Zalijepi lokaciju ovog mini pogleda u globalni pogled +STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Promijeni glavni pogled +STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Kopiraj lokaciju ovog mini pogleda u glavni pogled # Game options window STR_GAME_OPTIONS_CAPTION :{WHITE}Postavke igre @@ -2384,6 +2384,7 @@ STR_LINKGRAPH_LEGEND_CAPTION :{BLACK}Kazalo p STR_LINKGRAPH_LEGEND_ALL :{BLACK}Sve STR_LINKGRAPH_LEGEND_NONE :{BLACK}Ništa STR_LINKGRAPH_LEGEND_SELECT_COMPANIES :{BLACK}Odaberi tvrtke koje će se prikazati +STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP :{BLACK}{STRING}{}{COMPANY} # Linkgraph legend window and linkgraph legend in smallmap STR_LINKGRAPH_LEGEND_UNUSED :{TINY_FONT}{BLACK}nekorišten @@ -3076,6 +3077,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Unesi im STR_TOWN_DIRECTORY_CAPTION :{WHITE}Gradovi STR_TOWN_DIRECTORY_NONE :{ORANGE}- Ništa - STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Grad){BLACK} ({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Imena gradova - klikni na ime kako bi centrirao pogled na grad. Ctrl+klik otvara novi prozor sa lokacijom grada STR_TOWN_POPULATION :{BLACK}Svjetsko stanovništvo: {COMMA} @@ -3083,8 +3085,7 @@ STR_TOWN_POPULATION :{BLACK}Svjetsko STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Metropola) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Stanovništvo: {ORANGE}{COMMA}{BLACK} Kuće: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Putnika prošli mjesec: {ORANGE}{COMMA}{BLACK} najviše: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Pošte prošli mjesec: {ORANGE}{COMMA}{BLACK} najviše: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} prošli mjesec: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Potrebno tereta za rast grada: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} potrebno STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} potrebno zimi @@ -4331,6 +4332,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ovo STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... cesta je orijentirana u krivom smjeru STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... prolazne postaje ne mogu imati zavoje STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... prolazne postaje ne mogu imati raskrižja +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... cesta je jednosmjerna ili je blokirana # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Nije moguće ukloniti dio postaje... @@ -4582,6 +4584,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Originalni zvuk STR_BASESOUNDS_WIN_DESCRIPTION :Originalni zvukovi za Transport Tycoon Deluxe Windows izdanje. STR_BASESOUNDS_NONE_DESCRIPTION :Zvučni paket bez ikakvih zvukova. STR_BASEMUSIC_WIN_DESCRIPTION :Originalna glazba za Transport Tycoon Deluxe Windows izdanje. +STR_BASEMUSIC_DOS_DESCRIPTION :Originalna glazba za Transport Tycoon Deluxe DOS izdanje. +STR_BASEMUSIC_TTO_DESCRIPTION :Originalna glazba za Transport Tycoon (original/editor svijeta) DOS izdanje. STR_BASEMUSIC_NONE_DESCRIPTION :Glazbeni paket bez ikakve glazbe. ##id 0x2000 diff --git a/src/lang/czech.txt b/src/lang/czech.txt index 2261212ab3..901aa6c280 100644 --- a/src/lang/czech.txt +++ b/src/lang/czech.txt @@ -3070,8 +3070,6 @@ STR_TOWN_POPULATION :{BLACK}Populace STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (velkoměsto) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Populace: {ORANGE}{COMMA}{BLACK} Domů: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Cestujících minulý měsíc: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Pošta za minulý měsíc: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Množství doručeného nákladu potřebného pro rozvoj města: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{RED}Je potřeba {ORANGE}{STRING} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} vyžadováno v zimě diff --git a/src/lang/danish.txt b/src/lang/danish.txt index 4a92024575..9dcc55c1f8 100644 --- a/src/lang/danish.txt +++ b/src/lang/danish.txt @@ -2977,8 +2977,6 @@ STR_TOWN_POPULATION :{BLACK}Verdens STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (by) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Indbyggere: {ORANGE}{COMMA}{BLACK} Huse: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passagerer sidste måned: {ORANGE}{COMMA}{BLACK} maks.: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Post sidste måned: {ORANGE}{COMMA}{BLACK} maks.: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Nødvendig godsmængde for at byen kan vokse: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} krævet STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} kræves om vinteren diff --git a/src/lang/dutch.txt b/src/lang/dutch.txt index ad2bfe4722..764a0fc106 100644 --- a/src/lang/dutch.txt +++ b/src/lang/dutch.txt @@ -2977,8 +2977,6 @@ STR_TOWN_POPULATION :{BLACK}Wereldbe STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Groeistad) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Bevolking: {ORANGE}{COMMA}{BLACK} Huizen: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passagiers afgelopen maand: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Post afgelopen maand: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Vracht nodig voor groei van stad: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} noodzakelijk STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} noodzakelijk in de winter diff --git a/src/lang/english.txt b/src/lang/english.txt index c844d0b0c6..5344ef7d41 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -881,10 +881,10 @@ STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLAC # Extra view window STR_EXTRA_VIEW_PORT_TITLE :{WHITE}Viewport {COMMA} -STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Copy to viewport +STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Change viewport STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT :{BLACK}Copy the location of the main view to this viewport -STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Paste from viewport -STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Paste the location of this viewport to the main view +STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Change main view +STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Copy the location of this viewport to the main view # Game options window STR_GAME_OPTIONS_CAPTION :{WHITE}Game Options @@ -2288,6 +2288,7 @@ STR_LINKGRAPH_LEGEND_CAPTION :{BLACK}Cargo Fl STR_LINKGRAPH_LEGEND_ALL :{BLACK}All STR_LINKGRAPH_LEGEND_NONE :{BLACK}None STR_LINKGRAPH_LEGEND_SELECT_COMPANIES :{BLACK}Select companies to be displayed +STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP :{BLACK}{STRING}{}{COMPANY} # Linkgraph legend window and linkgraph legend in smallmap STR_LINKGRAPH_LEGEND_UNUSED :{TINY_FONT}{BLACK}unused @@ -2980,6 +2981,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Enter a STR_TOWN_DIRECTORY_CAPTION :{WHITE}Towns STR_TOWN_DIRECTORY_NONE :{ORANGE}- None - STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (City){BLACK} ({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Town names - click on name to centre main view on town. Ctrl+Click opens a new viewport on town location STR_TOWN_POPULATION :{BLACK}World population: {COMMA} @@ -2987,8 +2989,7 @@ STR_TOWN_POPULATION :{BLACK}World po STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (City) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Population: {ORANGE}{COMMA}{BLACK} Houses: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passengers last month: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Mail last month: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} last month: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Cargo needed for town growth: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} required STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} required in winter @@ -4235,6 +4236,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... this STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... road facing in the wrong direction STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... drive through stops can't have corners STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... drive through stops can't have junctions +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... road is one way or blocked # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Can't remove part of station... @@ -4486,6 +4488,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Original Transp STR_BASESOUNDS_WIN_DESCRIPTION :Original Transport Tycoon Deluxe Windows edition sounds. STR_BASESOUNDS_NONE_DESCRIPTION :A sound pack without any sounds. STR_BASEMUSIC_WIN_DESCRIPTION :Original Transport Tycoon Deluxe Windows edition music. +STR_BASEMUSIC_DOS_DESCRIPTION :Original Transport Tycoon Deluxe DOS edition music. +STR_BASEMUSIC_TTO_DESCRIPTION :Original Transport Tycoon (Original/World Editor) DOS edition music. STR_BASEMUSIC_NONE_DESCRIPTION :A music pack without actual music. ##id 0x2000 diff --git a/src/lang/english_AU.txt b/src/lang/english_AU.txt index 6a1f52fe66..20e4a4cf44 100644 --- a/src/lang/english_AU.txt +++ b/src/lang/english_AU.txt @@ -2940,8 +2940,6 @@ STR_TOWN_POPULATION :{BLACK}World po STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (City) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Population: {ORANGE}{COMMA}{BLACK} Houses: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passengers last month: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Mail last month: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Cargo needed for town growth: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} required STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} required in winter diff --git a/src/lang/english_US.txt b/src/lang/english_US.txt index e859e138f4..3d12c65077 100644 --- a/src/lang/english_US.txt +++ b/src/lang/english_US.txt @@ -2977,8 +2977,6 @@ STR_TOWN_POPULATION :{BLACK}World po STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (City) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Population: {ORANGE}{COMMA}{BLACK} Houses: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passengers last month: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Mail last month: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Cargo needed for town growth: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} required STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} required in winter diff --git a/src/lang/esperanto.txt b/src/lang/esperanto.txt index 0091c95ef9..f63f762019 100644 --- a/src/lang/esperanto.txt +++ b/src/lang/esperanto.txt @@ -2509,8 +2509,6 @@ STR_TOWN_POPULATION :{BLACK}Monda en STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Urbo) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Enloĝantoj: {ORANGE}{COMMA}{BLACK} Domoj: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Pasaĝeroj lastmonate: {ORANGE}{COMMA}{BLACK} maksimume: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Poŝto lastmonate: {ORANGE}{COMMA}{BLACK} maksimume: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Kargo bezonata por kreskigi urbon: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} bezonatas STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} bezonatas en vintro diff --git a/src/lang/estonian.txt b/src/lang/estonian.txt index bb63d32c4d..dd1e7effa8 100644 --- a/src/lang/estonian.txt +++ b/src/lang/estonian.txt @@ -3031,8 +3031,6 @@ STR_TOWN_POPULATION :{BLACK}Maailma STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Linn) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Rahvaarv: {ORANGE}{COMMA}{BLACK} Ehitisi: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Reisijaid eelmisel kuul: {ORANGE}{COMMA}{BLACK} Enim: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Posti eelmisel kuul: {ORANGE}{COMMA}{BLACK} Enim: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Veoseid linna kasvamiseks: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} vajalik STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} on talvel vajalik diff --git a/src/lang/faroese.txt b/src/lang/faroese.txt index d0f8e22527..9a17e2387b 100644 --- a/src/lang/faroese.txt +++ b/src/lang/faroese.txt @@ -2643,8 +2643,6 @@ STR_TOWN_POPULATION :{BLACK}Heims f STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Býur) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Fólkatal: {ORANGE}{COMMA}{BLACK} Hús: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Ferðafólk síðsta mánað: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Postur síðsta mánað: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Farmur ið tørvast fyri bygda menning: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} krevst STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} krevst um veturin diff --git a/src/lang/finnish.txt b/src/lang/finnish.txt index 834380d258..39451916e3 100644 --- a/src/lang/finnish.txt +++ b/src/lang/finnish.txt @@ -2977,8 +2977,6 @@ STR_TOWN_POPULATION :{BLACK}Maailman STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (City) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Asukasluku: {ORANGE}{COMMA}{BLACK} Taloja: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Matkustajia viime kuussa: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Postia viime kuussa: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Kaupungin kasvuun tarvittava rahti: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} vaadittu STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} vaaditaan talvella diff --git a/src/lang/french.txt b/src/lang/french.txt index 70ae97f131..6a361172a9 100644 --- a/src/lang/french.txt +++ b/src/lang/french.txt @@ -882,10 +882,10 @@ STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLAC # Extra view window STR_EXTRA_VIEW_PORT_TITLE :{WHITE}Vue {COMMA} -STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Copier vers la vue -STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT :{BLACK}Copier l'emplacement de la vue principale vers cette vue-ci -STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Coller depuis la vue -STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Coller l'emplacement de cette vue-ci vers la vue principale +STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Modifier cette vue +STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT :{BLACK}Copier l'emplacement de la vue principale vers cette vue +STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Modifier la vue principale +STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Copier l'emplacement de cette vue vers la vue principale # Game options window STR_GAME_OPTIONS_CAPTION :{WHITE}Options @@ -2289,6 +2289,7 @@ STR_LINKGRAPH_LEGEND_CAPTION :{BLACK}Légende STR_LINKGRAPH_LEGEND_ALL :{BLACK}Toute STR_LINKGRAPH_LEGEND_NONE :{BLACK}Aucune STR_LINKGRAPH_LEGEND_SELECT_COMPANIES :{BLACK}Choisir les compagnies à afficher +STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP :{BLACK}{STRING}{}{COMPANY} # Linkgraph legend window and linkgraph legend in smallmap STR_LINKGRAPH_LEGEND_UNUSED :{TINY_FONT}{BLACK}inutilisé @@ -2981,6 +2982,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Entrer u STR_TOWN_DIRECTORY_CAPTION :{WHITE}Villes STR_TOWN_DIRECTORY_NONE :{ORANGE}− Aucune − STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Métropole){BLACK} ({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Noms des villes - Cliquer sur un nom pour centrer la vue principale sur la ville. Ctrl-clic pour ouvrir une nouvelle vue sur la ville. STR_TOWN_POPULATION :{BLACK}Population mondiale{NBSP}: {COMMA} @@ -2988,8 +2990,7 @@ STR_TOWN_POPULATION :{BLACK}Populati STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Métropole) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Population{NBSP}: {ORANGE}{COMMA}{BLACK} − Maisons{NBSP}: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passagers le mois dernier{NBSP}: {ORANGE}{COMMA}{BLACK} − max.{NBSP}: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Courrier le mois dernier{NBSP}: {ORANGE}{COMMA}{BLACK} − max.{NBSP}: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} le mois dernier{NBSP}: {ORANGE}{COMMA}{BLACK} - max.{NBSP}: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Cargaison nécessaire à la croissance de la ville{NBSP}: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} requis STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} requis en hiver @@ -4236,6 +4237,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... cett STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... mauvaise orientation de la route STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... les arrêts ne peuvent pas avoir de virages STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... les arrêts ne peuvent pas avoir de jonctions +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... la route est à sens unique ou bloquée # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Impossible de supprimer une partie de la gare... @@ -4487,6 +4489,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Sons originaux STR_BASESOUNDS_WIN_DESCRIPTION :Sons originaux de Transport Tycoon Deluxe (version Windows). STR_BASESOUNDS_NONE_DESCRIPTION :Un pack de sons sans sons. STR_BASEMUSIC_WIN_DESCRIPTION :Musiques originales de Transport Tycoon Deluxe (version Windows). +STR_BASEMUSIC_DOS_DESCRIPTION :Musiques originales de Transport Tycoon Deluxe (version DOS). +STR_BASEMUSIC_TTO_DESCRIPTION :Musiques originales de Transport Tycoon (version Originale/World Editor). STR_BASEMUSIC_NONE_DESCRIPTION :Un pack de musiques sans musiques. ##id 0x2000 diff --git a/src/lang/gaelic.txt b/src/lang/gaelic.txt index b68e68858c..f883d377d5 100644 --- a/src/lang/gaelic.txt +++ b/src/lang/gaelic.txt @@ -3218,8 +3218,6 @@ STR_TOWN_POPULATION :{BLACK}Sluagh a STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Mòr-bhaile) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Sluagh: {ORANGE}{COMMA}{BLACK} Taighean: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Taistealaich am mìos mu dheireadh: {ORANGE}{COMMA}{BLACK} as motha: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Post am mìos mu dheireadh: {ORANGE}{COMMA}{BLACK} as motha: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Carago a tha a dhìth ach am fàs am baile: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{RED}Feum air {ORANGE}{STRING} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{BLACK}Feum air {ORANGE}{STRING}{BLACK} sa gheamhradh diff --git a/src/lang/galician.txt b/src/lang/galician.txt index f6058086d6..25c6313e56 100644 --- a/src/lang/galician.txt +++ b/src/lang/galician.txt @@ -2978,8 +2978,6 @@ STR_TOWN_POPULATION :{BLACK}Poboaci STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Cidade) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Poboación: {ORANGE}{COMMA}{BLACK} Casas: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Pasaxeiros último mes: {ORANGE}{COMMA}{BLACK} máx: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Correo último mes: {ORANGE}{COMMA}{BLACK} máx: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Carga necesaria para o crecemento da cidade: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} necesario STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} necesarios en inverno diff --git a/src/lang/german.txt b/src/lang/german.txt index 71e8f14b9c..e91d83c282 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -2978,8 +2978,6 @@ STR_TOWN_POPULATION :{BLACK}Weltbev STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Großstadt) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Einwohner: {ORANGE}{COMMA}{BLACK} Häuser: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passagiere im letzten Monat: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Postsäcke im letzten Monat: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Für Stadtwachstum benötigte Fracht: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} benötigt STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} im Winter benötigt diff --git a/src/lang/greek.txt b/src/lang/greek.txt index 74b5538f1b..548a7caa29 100644 --- a/src/lang/greek.txt +++ b/src/lang/greek.txt @@ -3092,8 +3092,7 @@ STR_TOWN_POPULATION :{BLACK}Παγκ STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Πόλη) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Πληθυσμός: {ORANGE}{COMMA}{BLACK} Σπίτια: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Επιβάτες τον προηγούμενο μήνα: {ORANGE}{COMMA}{BLACK} μεγ: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Αλληλογραφία τον προηγούμενο μήνα: {ORANGE}{COMMA}{BLACK} μεγ: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} προηγούμενος μήνας: {ORANGE}{COMMA}{BLACK} μέγιστο: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Εμπορεύματα που χρειάζονται για την επέκταση της πόλης: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} απαιτείται STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} απαιτείται τον χειμώνα @@ -4346,6 +4345,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... αυ STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... ο δρόμος βλέπει σε λάθος κατεύθυνση STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... οι μη τερματικοί σταθμοί δε μπορούν να έχουν στροφές STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... οι μη τερματικοί σταθμοί δε μπορούν να έχουν διασταυρώσεις +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... ο δρόμος είναι μονόδρομος η μπλοκαρισμένος # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Δεν μπορεί να αφαιρεθεί μέρος του σταθμού... @@ -4597,6 +4597,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Αρχικοί STR_BASESOUNDS_WIN_DESCRIPTION :Αρχικοί ήχοι από το Transport Tycoon Deluxe έκδοση Windows. STR_BASESOUNDS_NONE_DESCRIPTION :Ένα πάκετο ήχων χώρις ήχους. STR_BASEMUSIC_WIN_DESCRIPTION :Αρχική μουσική από το Transport Tycoon Deluxe έκδοση Windows. +STR_BASEMUSIC_DOS_DESCRIPTION :Αρχική μουσική από το Transport Tycoon Deluxe έκδοση DOS. +STR_BASEMUSIC_TTO_DESCRIPTION :Αρχική μουσική από το Transport Tycoon (Αρχικός Επεξεργαστής Κόσμου) έκδοση DOS. STR_BASEMUSIC_NONE_DESCRIPTION :Ένα πάκετο μουσικής χωρίς πραγματική μουσική. ##id 0x2000 diff --git a/src/lang/hebrew.txt b/src/lang/hebrew.txt index b569c54fa0..c0dc65f20e 100644 --- a/src/lang/hebrew.txt +++ b/src/lang/hebrew.txt @@ -2991,8 +2991,6 @@ STR_TOWN_POPULATION :{BLACK}אוכל STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (עיר) STR_TOWN_VIEW_POPULATION_HOUSES :{ORANGE}{1:COMMA}{BLACK} :בתים {ORANGE}{0:COMMA}{BLACK} :אוכלוסיה -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{ORANGE}{1:COMMA}{BLACK} :מספר מירבי {ORANGE}{0:COMMA}{BLACK} :נוסעים בחודש שעבר -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{ORANGE}{1:COMMA}{BLACK} :מספר מירבי {ORANGE}{0:COMMA}{BLACK} :שקי דואר בחודש שעבר STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}המטען שצריך בשביל גידול עיר STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} דרוש STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} דרוש בחורף diff --git a/src/lang/hungarian.txt b/src/lang/hungarian.txt index 85684dbb07..7f910eba1a 100644 --- a/src/lang/hungarian.txt +++ b/src/lang/hungarian.txt @@ -3041,8 +3041,6 @@ STR_TOWN_POPULATION :{BLACK}Világn STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Város) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Lakosság: {ORANGE}{COMMA}{BLACK} Házak: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Utasok az előző hónapban: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Levélcsomagok az előző hónapban: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}A város növekedéséhez szükséges rakomány: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} szükséges STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} szükséges télen diff --git a/src/lang/icelandic.txt b/src/lang/icelandic.txt index 479bca6a76..3fe5319773 100644 --- a/src/lang/icelandic.txt +++ b/src/lang/icelandic.txt @@ -2801,8 +2801,6 @@ STR_TOWN_POPULATION :{BLACK}Heildar STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Borg) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Íbúafjöldi: {ORANGE}{COMMA}{BLACK} Hús: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Farþegar síðasta mánaðar: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Póstur síðasta mánaðar: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Nauðsynlegur farmur fyrir stækkun bæjarins: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} nauðsynlegt STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} nauðsynlegt á veturnar diff --git a/src/lang/indonesian.txt b/src/lang/indonesian.txt index 09b8dbb664..7c8c417005 100644 --- a/src/lang/indonesian.txt +++ b/src/lang/indonesian.txt @@ -2971,8 +2971,6 @@ STR_TOWN_POPULATION :{BLACK}Populasi STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (City) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Populasi: {ORANGE}{COMMA}{BLACK} Rumah: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Penumpang bulan lalu: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Surat bulan lalu: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Kargo untuk pertumbuhan kota: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{RED} Butuh {ORANGE}{STRING} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} dibutuhkan saat musim dingin diff --git a/src/lang/irish.txt b/src/lang/irish.txt index c94c303d52..18f898313b 100644 --- a/src/lang/irish.txt +++ b/src/lang/irish.txt @@ -2973,8 +2973,6 @@ STR_TOWN_POPULATION :{BLACK}Daonra d STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Cathair) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Daonra: {ORANGE}{COMMA}{BLACK} Tithe: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Paisinéirí an mhí seo caite: {ORANGE}{COMMA}{BLACK} Uasmhéid: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Post an mhí seo caite: {ORANGE}{COMMA}{BLACK} uasmhéid: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Lastas atá ag teastáil le go bhfásfaidh an baile: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} ag teastáil STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} ag teastáil sa Gheimhreadh diff --git a/src/lang/italian.txt b/src/lang/italian.txt index 0759f850e1..d245136f1d 100644 --- a/src/lang/italian.txt +++ b/src/lang/italian.txt @@ -883,9 +883,9 @@ STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLAC # Extra view window STR_EXTRA_VIEW_PORT_TITLE :{WHITE}Mini visuale {COMMA} -STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Copia nella mini visuale +STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN :{BLACK}Cambia mini visuale STR_EXTRA_VIEW_MOVE_VIEW_TO_MAIN_TT :{BLACK}Copia la posizione della visuale principale in questa mini visuale -STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Copia dalla mini visuale +STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW :{BLACK}Cambia visuale principale STR_EXTRA_VIEW_MOVE_MAIN_TO_VIEW_TT :{BLACK}Copia la posizione di questa mini visuale nella visuale principale # Game options window @@ -2314,6 +2314,7 @@ STR_LINKGRAPH_LEGEND_NONE :{BLACK}Nessuno STR_LINKGRAPH_LEGEND_NONE.ms :{BLACK}Nessuno STR_LINKGRAPH_LEGEND_NONE.fs :{BLACK}Nessuna STR_LINKGRAPH_LEGEND_SELECT_COMPANIES :{BLACK}Seleziona le compagnie da mostrare +STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP :{BLACK}{STRING}{}{COMPANY} # Linkgraph legend window and linkgraph legend in smallmap STR_LINKGRAPH_LEGEND_UNUSED :{TINY_FONT}{BLACK}inutilizzata @@ -3010,6 +3011,7 @@ STR_EDIT_SIGN_SIGN_OSKTITLE :{BLACK}Inserire STR_TOWN_DIRECTORY_CAPTION :{WHITE}Città STR_TOWN_DIRECTORY_NONE :{ORANGE}- Nessuna - STR_TOWN_DIRECTORY_TOWN :{ORANGE}{TOWN}{BLACK} ({COMMA}) +STR_TOWN_DIRECTORY_CITY :{ORANGE}{TOWN}{YELLOW} (Metropoli){BLACK} ({COMMA}) STR_TOWN_DIRECTORY_LIST_TOOLTIP :{BLACK}Nomi delle città - fare clic su un nome per centrare la visuale principale sulla città. CTRL+clic la mostra in una mini visuale. STR_TOWN_POPULATION :{BLACK}Popolazione mondiale: {COMMA} @@ -3017,8 +3019,7 @@ STR_TOWN_POPULATION :{BLACK}Popolazi STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Metropoli) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Popolazione: {ORANGE}{COMMA}{BLACK} Case: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passeggeri il mese scorso: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Posta il mese scorso: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} il mese scorso: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Carichi richiesti per la crescita della città: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} richiest{G 0 o o a} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} richiest{G 0 o o a} in inverno @@ -4265,6 +4266,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ques STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... strada rivolta nella direzione sbagliata STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... le fermate passanti non possono trovarsi in curva STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... le fermate passanti non possono avere raccordi +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... la strada è bloccata o a senso unico # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Impossibile rimuovere parte della stazione... @@ -4516,6 +4518,8 @@ STR_BASESOUNDS_DOS_DESCRIPTION :Suoni originali STR_BASESOUNDS_WIN_DESCRIPTION :Suoni originali di Transport Tycoon Deluxe, edizione Windows. STR_BASESOUNDS_NONE_DESCRIPTION :Un pacchetto sonoro non contenente alcun suono. STR_BASEMUSIC_WIN_DESCRIPTION :Musica originale di Transport Tycoon Deluxe, edizione Windows. +STR_BASEMUSIC_DOS_DESCRIPTION :Musica originale di Transport Tycoon Deluxe, edizione DOS. +STR_BASEMUSIC_TTO_DESCRIPTION :Musica originale di Transport Tycoon (edizione Original/World). STR_BASEMUSIC_NONE_DESCRIPTION :Un pacchetto musicale non contenente alcuna musica. ##id 0x2000 diff --git a/src/lang/japanese.txt b/src/lang/japanese.txt index 8b0f040b06..c9fd7a2c3f 100644 --- a/src/lang/japanese.txt +++ b/src/lang/japanese.txt @@ -2974,8 +2974,6 @@ STR_TOWN_POPULATION :{BLACK}地域 STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN}(市) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}人口: {ORANGE}{COMMA}人{BLACK} 建物: {ORANGE}{COMMA}戸 -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}旅客数(先月): {ORANGE}{COMMA}人{BLACK} 最大: {ORANGE}{COMMA}人 -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}郵便袋(先月): {ORANGE}{COMMA}袋{BLACK} 最大: {ORANGE}{COMMA}袋 STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}街の成長に必要な物資: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}が{RED}必要です STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}が{BLACK}冬に必要です diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 4d134b60c5..b8d8cf1c4c 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -651,6 +651,7 @@ STR_MUSIC_RULER_MARKER :{TINY_FONT}{BLA STR_MUSIC_TRACK_NONE :{TINY_FONT}{DKGREEN}-- STR_MUSIC_TRACK_DIGIT :{TINY_FONT}{DKGREEN}{ZEROFILL_NUM} STR_MUSIC_TITLE_NONE :{TINY_FONT}{DKGREEN}------ +STR_MUSIC_TITLE_NOMUSIC :{TINY_FONT}{DKGREEN}사용 가능한 음악 없음 STR_MUSIC_TITLE_NAME :{TINY_FONT}{DKGREEN}"{STRING}" STR_MUSIC_TRACK :{TINY_FONT}{BLACK}트랙 STR_MUSIC_XTITLE :{TINY_FONT}{BLACK}제목 @@ -671,11 +672,14 @@ STR_MUSIC_TOOLTIP_TOGGLE_PROGRAM_SHUFFLE :{BLACK}프로 STR_MUSIC_TOOLTIP_SHOW_MUSIC_TRACK_SELECTION :{BLACK}배경 음악 트랙을 선택할 수 있는 창을 엽니다. # Playlist window +STR_PLAYLIST_MUSIC_SELECTION_SETNAME :{WHITE}음악 프로그램 - '{STRING}' STR_PLAYLIST_TRACK_NAME :{TINY_FONT}{LTBLUE}{ZEROFILL_NUM} "{STRING}" STR_PLAYLIST_TRACK_INDEX :{TINY_FONT}{BLACK}음악 목록 STR_PLAYLIST_PROGRAM :{TINY_FONT}{BLACK}프로그램 - '{STRING}' STR_PLAYLIST_CLEAR :{TINY_FONT}{BLACK}초기화 +STR_PLAYLIST_CHANGE_SET :{BLACK}세트 변경 STR_PLAYLIST_TOOLTIP_CLEAR_CURRENT_PROGRAM_CUSTOM1 :{BLACK}현재 배경 음악 프로그램을 초기화합니다. (사용자1, 사용자2에서만 선택 가능) +STR_PLAYLIST_TOOLTIP_CHANGE_SET :{BLACK}배경 음악 세트를 이미 설치된 다른 세트로 변경합니다. STR_PLAYLIST_TOOLTIP_CLICK_TO_ADD_TRACK :{BLACK}배경 음악 목록에 음악을 추가하려면 클릭하세요. (사용자1, 사용자2에서만 사용 가능) STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK :{BLACK}배경 음악 목록에서 음악을 제거하려면 클릭하세요. (사용자1, 사용자2에서만 사용 가능) @@ -1335,6 +1339,12 @@ STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_HELPTEXT :소형지도 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_GREEN :녹색 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_DARK_GREEN :어두운 녹색 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_VIOLET :보라색 +STR_CONFIG_SETTING_SCROLLMODE :외부 화면 스크롤 행동: {STRING} +STR_CONFIG_SETTING_SCROLLMODE_HELPTEXT :지도를 스크롤 할 때의 행동을 선택합니다. +STR_CONFIG_SETTING_SCROLLMODE_DEFAULT :마우스 오른쪽 클릭으로 외부 화면 이동 (마우스 위치는 고정) +STR_CONFIG_SETTING_SCROLLMODE_RMB_LOCKED :마우스 오른쪽 클릭으로 지도 이동 (마우스 위치는 고정) +STR_CONFIG_SETTING_SCROLLMODE_RMB :마우스 오른쪽 클릭으로 지도 이동 +STR_CONFIG_SETTING_SCROLLMODE_LMB :마우스 왼쪽 클릭으로 지도 이동 STR_CONFIG_SETTING_SMOOTH_SCROLLING :게임 화면을 이동시킬 때 부드럽게 이동: {STRING} STR_CONFIG_SETTING_SMOOTH_SCROLLING_HELPTEXT :소형 지도를 클릭하여 특정 지역으로 스크롤되거나 지도 상의 특정 장소로 이동하는 경우에 주 화면이 어떻게 스크롤 되는지를 설정합니다. 이 설정을 켜면, 화면이 목표 지점까지 부드럽게 이동하고, 설정을 끄면 목표 지점으로 곧바로 넘어가게 됩니다. STR_CONFIG_SETTING_MEASURE_TOOLTIP :건설도구 사용시 거리 도움말 표시: {STRING} @@ -1347,7 +1357,7 @@ STR_CONFIG_SETTING_LIVERIES_ALL :모든 회사 STR_CONFIG_SETTING_PREFER_TEAMCHAT :엔터(ENTER) 키로 같은 팀끼리 채팅: {STRING} STR_CONFIG_SETTING_PREFER_TEAMCHAT_HELPTEXT :이 설정을 켜면, 멀티 플레이시 같은 회사 간의 채팅을 키 대신 키로 할 수 있게 됩니다. STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING :마우스 휠 동작: {STRING} -STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING_HELPTEXT :상하좌우로 회전 가능한 마우스 휠(2차원 마우스휠)로 지도를 스크롤할 수 있게 허용합니다. +STR_CONFIG_SETTING_SCROLLWHEEL_SCROLLING_HELPTEXT :상하좌우로 회전 가능한 마우스 휠(2차원 마우스 휠)로 지도를 스크롤할 수 있게 허용합니다. STR_CONFIG_SETTING_SCROLLWHEEL_ZOOM :화면 확대/축소 STR_CONFIG_SETTING_SCROLLWHEEL_SCROLL :지도 스크롤 STR_CONFIG_SETTING_SCROLLWHEEL_OFF :끄기 @@ -2978,8 +2988,7 @@ STR_TOWN_POPULATION :{BLACK}총 인 STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (대도시) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}인구: {ORANGE}{COMMA}{BLACK} 가구수: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}지난 달 승객 수: {ORANGE}{COMMA}{BLACK} 최대: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}지난달 우편수: {ORANGE}{COMMA}{BLACK} 최고: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} 지난 달: {ORANGE}{COMMA}{BLACK} 최대: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}도시가 성장하기 위해 필요한 화물: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED}{G 0 "이" "가"} 필요함 STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :겨울에는 {ORANGE}{STRING}{BLACK}{G 0 "이" "가"} 필요함 @@ -4226,6 +4235,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... 여 STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... 도로의 방향과 일치하지 않습니다. STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... 도로 통과 정류장은 곡선도로에 건설할 수 없습니다. STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... 도로 통과 정류장은 교차로에 건설할 수 없습니다. +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... 도로가 일방통행이거나 막혔습니다 # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}역의 일부를 제거할 수 없습니다... @@ -4473,10 +4483,12 @@ STR_DESKTOP_SHORTCUT_COMMENT :트랜스포트 STR_BASEGRAPHICS_DOS_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 도스 에디션의 그래픽입니다. STR_BASEGRAPHICS_DOS_DE_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 도스 에디션(독일)의 그래픽입니다. STR_BASEGRAPHICS_WIN_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 윈도 에디션의 그래픽입니다. -STR_BASESOUNDS_DOS_DESCRIPTION :오리지널 트랜스포트 타이쿤 도스 에디션의 효과음입니다. +STR_BASESOUNDS_DOS_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 도스 에디션의 효과음입니다. STR_BASESOUNDS_WIN_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 윈도 에디션의 효과음입니다. STR_BASESOUNDS_NONE_DESCRIPTION :아무런 효과음도 없는 효과음 팩입니다. STR_BASEMUSIC_WIN_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 윈도 에디션의 음악입니다. +STR_BASEMUSIC_DOS_DESCRIPTION :오리지널 트랜스포트 타이쿤 디럭스 DOS 에디션의 음악입니다. +STR_BASEMUSIC_TTO_DESCRIPTION :오리지널 트랜스포트 타이쿤 (오리지널/월드 에디터) DOS 에디션의 음악입니다. STR_BASEMUSIC_NONE_DESCRIPTION :실제 음악이 없는 음악 목록입니다. ##id 0x2000 diff --git a/src/lang/latin.txt b/src/lang/latin.txt index 33b09d3863..59f41144e8 100644 --- a/src/lang/latin.txt +++ b/src/lang/latin.txt @@ -3179,8 +3179,6 @@ STR_TOWN_POPULATION :{BLACK}Incolae STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Urbs) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Incolae: {ORANGE}{COMMA}{BLACK} Aedificia: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Vectores mensis prioris: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Epistulae mensis prioris: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Onera mandata ad oppidum crescendum: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} mandatur STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} hieme mandatur diff --git a/src/lang/latvian.txt b/src/lang/latvian.txt index 7b89b06c81..134bd05746 100644 --- a/src/lang/latvian.txt +++ b/src/lang/latvian.txt @@ -2908,8 +2908,6 @@ STR_TOWN_POPULATION :{BLACK}Pasaules STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (lielpilsēta) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Iedzīvotāji: {ORANGE}{COMMA}{BLACK} Mājas: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Pasažieri pagājušajā mēnesī: {ORANGE}{COMMA}{BLACK} maksimāli: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Pasts pagājušajā mēnesī: {ORANGE}{COMMA}{BLACK} maksimāli: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Krava nepieciešama pilsētas attīstībai: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} nepieciešams STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} nepieciešams ziemā diff --git a/src/lang/lithuanian.txt b/src/lang/lithuanian.txt index 9b57e271b3..fb1fe0b4d9 100644 --- a/src/lang/lithuanian.txt +++ b/src/lang/lithuanian.txt @@ -3192,8 +3192,6 @@ STR_TOWN_POPULATION :{BLACK}Pasaulio STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Miestas) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Populiacija: {ORANGE}{COMMA}{BLACK} Namų skaičius: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Praėjusio mėnesio keleivių sk.: {ORANGE}{COMMA}{BLACK} Daugiausia: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Praėjusio mėnesio pašto siuntų sk.: {ORANGE}{COMMA}{BLACK} Daugiausia: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Kad miestas augtų reikalingi kroviniai: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} reikia STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} reikalingas žiemą diff --git a/src/lang/luxembourgish.txt b/src/lang/luxembourgish.txt index 639cd04674..324bacc924 100644 --- a/src/lang/luxembourgish.txt +++ b/src/lang/luxembourgish.txt @@ -2977,8 +2977,6 @@ STR_TOWN_POPULATION :{BLACK}Weltbev STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Stad) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Awunner: {ORANGE}{COMMA}{BLACK} Haiser: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passagéier leschte Mount: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Post leschte Mount: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Gidder gebraucht fir Stadwuesstem: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} gebraucht STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} gebraucht am Wanter diff --git a/src/lang/malay.txt b/src/lang/malay.txt index a40afb0df8..1bcb8156f0 100644 --- a/src/lang/malay.txt +++ b/src/lang/malay.txt @@ -2681,8 +2681,6 @@ STR_TOWN_POPULATION :{BLACK}Jumlah p STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Bandaraya) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Jumlah penduduk: {ORANGE}{COMMA}{BLACK} Rumah: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Penumpang bulan lalu: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Beg surat bulan lepas: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Kargi yang diperlukan untuk pembesaran bandar: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} diperlukan STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} diperlukan sewaktu musim sejuk diff --git a/src/lang/norwegian_bokmal.txt b/src/lang/norwegian_bokmal.txt index 09002ff88d..052fb8bf22 100644 --- a/src/lang/norwegian_bokmal.txt +++ b/src/lang/norwegian_bokmal.txt @@ -2981,8 +2981,6 @@ STR_TOWN_POPULATION :{BLACK}Verdensb STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (By) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Innbyggertall: {ORANGE}{COMMA}{BLACK} Antall hus: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passasjerer forrige måned: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Post forrige måned: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Varebehov for byvekst: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} påkrevd STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} nødvendig om vinteren diff --git a/src/lang/norwegian_nynorsk.txt b/src/lang/norwegian_nynorsk.txt index ba51b11393..4495271900 100644 --- a/src/lang/norwegian_nynorsk.txt +++ b/src/lang/norwegian_nynorsk.txt @@ -2892,8 +2892,6 @@ STR_TOWN_POPULATION :{BLACK}Verdsinn STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (By) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Innbyggjartal: {ORANGE}{COMMA}{BLACK} Antal hus: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passasjerar førre månad: {ORANGE}{COMMA}{BLACK} maks.: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Post førre månad: {ORANGE}{COMMA}{BLACK} maks.: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Varer naudsynt for folketalsauke: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} naudsynt STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} naudsynt om vinteren diff --git a/src/lang/polish.txt b/src/lang/polish.txt index 8c89991762..36ad07d926 100644 --- a/src/lang/polish.txt +++ b/src/lang/polish.txt @@ -3362,8 +3362,7 @@ STR_TOWN_POPULATION :{BLACK}Populacj STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Miasto) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Populacja: {ORANGE}{COMMA}{BLACK} Domów: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Pasażerów w zeszłym miesiącu: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Poczta w zeszłym miesiącu: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} w ostatnim miesiącu: {ORANGE}{COMMA}{BLACK} najwięcej: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Towar potrzebny do rozwoju miasta: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{RED}Wymagana {ORANGE}{STRING} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} wymagane zimą @@ -4616,6 +4615,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... ta d STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... droga jest zorientowana w złym kierunku STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... przystanki przelotowe nie mogą mieć zakrętów STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... przystanki przelotowe nie mogą mieć skrzyżowań +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... droga jest jednokierunkowa lub zablokowana # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Nie można usunąć części stacji... diff --git a/src/lang/portuguese.txt b/src/lang/portuguese.txt index ef966a4746..82662903f8 100644 --- a/src/lang/portuguese.txt +++ b/src/lang/portuguese.txt @@ -2978,8 +2978,6 @@ STR_TOWN_POPULATION :{BLACK}Populaç STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Metrópole) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}População: {ORANGE}{COMMA}{BLACK} Casas: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passageiros no último mês: {ORANGE}{COMMA}{BLACK} máx: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Correio no último mês: {ORANGE}{COMMA}{BLACK} máx: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Mercadoria necessária para o seu desenvolvimento: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{RED}É necessário {ORANGE}{STRING} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{BLACK}No inverno, é necessário {ORANGE}{STRING} diff --git a/src/lang/romanian.txt b/src/lang/romanian.txt index e4a8e4f7a5..0871b75ae4 100644 --- a/src/lang/romanian.txt +++ b/src/lang/romanian.txt @@ -2932,8 +2932,6 @@ STR_TOWN_POPULATION :{BLACK}Populaţ STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Metropolă) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Populaţia: {ORANGE}{COMMA}{BLACK} Locuinţe: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Călători luna trecută: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Colete poştale luna trecută: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Transporturi necesare dezvoltării oraşului: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} necesare STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} necesare iarna diff --git a/src/lang/russian.txt b/src/lang/russian.txt index d584393c1e..b8c8037e64 100644 --- a/src/lang/russian.txt +++ b/src/lang/russian.txt @@ -3167,8 +3167,7 @@ STR_TOWN_POPULATION :{BLACK}Насе STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Мегаполис) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Население: {ORANGE}{COMMA}{BLACK} Зданий: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Пассажиров в прошлом месяце: {ORANGE}{COMMA}{BLACK}; макс.: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Почты в прошлом месяце: {ORANGE}{COMMA}{BLACK}; макс.: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} в прошлом месяце: {ORANGE}{COMMA}{BLACK} Макс.: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Груз, необходимый для роста города: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} требу{G 0 е е е ю}тся STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} требу{G 0 е е е ю}тся зимой @@ -4425,6 +4424,7 @@ STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD :{WHITE}... эт STR_ERROR_DRIVE_THROUGH_DIRECTION :{WHITE}... неверное направление дороги STR_ERROR_DRIVE_THROUGH_CORNER :{WHITE}... на проходных остановках нельзя делать повороты STR_ERROR_DRIVE_THROUGH_JUNCTION :{WHITE}... на проходных остановках нельзя делать перекрёстки +STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD :{WHITE}... дорога односторонняя или заблокирована # Station destruction related errors STR_ERROR_CAN_T_REMOVE_PART_OF_STATION :{WHITE}Невозможно удалить часть станции... @@ -4493,7 +4493,7 @@ STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION :{WHITE}Недо STR_ERROR_MUST_REMOVE_SIGNALS_FIRST :{WHITE}Сначала удалите сигналы STR_ERROR_NO_SUITABLE_RAILROAD_TRACK :{WHITE}Нет подходящих рельсов STR_ERROR_MUST_REMOVE_RAILROAD_TRACK :{WHITE}Сначала удалите рельсы -STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}Дорога односторонняя или блокирована +STR_ERROR_CROSSING_ON_ONEWAY_ROAD :{WHITE}Дорога односторонняя или заблокирована STR_ERROR_CROSSING_DISALLOWED :{WHITE}Через этот вид рельсов запрещено строить переезды STR_ERROR_CAN_T_BUILD_SIGNALS_HERE :{WHITE}Здесь невозможно поставить сигнал... STR_ERROR_CAN_T_BUILD_RAILROAD_TRACK :{WHITE}Здесь невозможно проложить рельсы... @@ -4669,13 +4669,15 @@ STR_ERROR_CAN_T_DELETE_SIGN :{WHITE}Не у STR_DESKTOP_SHORTCUT_COMMENT :Экономический симулятор на основе игры «Transport Tycoon Deluxe» # Translatable descriptions in media/baseset/*.ob* files -STR_BASEGRAPHICS_DOS_DESCRIPTION :Оригинальная графика из Transport Tycoon Deluxe для DOS. -STR_BASEGRAPHICS_DOS_DE_DESCRIPTION :Оригинальная графика из немецкой версии Transport Tycoon Deluxe для DOS. -STR_BASEGRAPHICS_WIN_DESCRIPTION :Оригинальная графика из Transport Tycoon Deluxe для Windows. -STR_BASESOUNDS_DOS_DESCRIPTION :Оригинальный набор звукового оформления из игры Transport Tycoon Deluxe для DOS. -STR_BASESOUNDS_WIN_DESCRIPTION :Оригинальный набор звукового оформления из игры Transport Tycoon Deluxe для Windows. +STR_BASEGRAPHICS_DOS_DESCRIPTION :Графика из Transport Tycoon Deluxe для DOS. +STR_BASEGRAPHICS_DOS_DE_DESCRIPTION :Графика из немецкой версии Transport Tycoon Deluxe для DOS. +STR_BASEGRAPHICS_WIN_DESCRIPTION :Графика из Transport Tycoon Deluxe для Windows. +STR_BASESOUNDS_DOS_DESCRIPTION :Набор звукового оформления из игры Transport Tycoon Deluxe для DOS. +STR_BASESOUNDS_WIN_DESCRIPTION :Набор звукового оформления из игры Transport Tycoon Deluxe для Windows. STR_BASESOUNDS_NONE_DESCRIPTION :"Пустой" набор звукового оформления, не содержащий никаких звуков. -STR_BASEMUSIC_WIN_DESCRIPTION :Оригинальный набор музыкального оформления из игры Transport Tycoon Deluxe для Windows. +STR_BASEMUSIC_WIN_DESCRIPTION :Набор музыкального оформления из игры Transport Tycoon Deluxe для Windows. +STR_BASEMUSIC_DOS_DESCRIPTION :Набор музыкального оформления из игры Transport Tycoon Deluxe для DOS. +STR_BASEMUSIC_TTO_DESCRIPTION :Набор музыкального оформления из игры Transport Tycoon Deluxe для DOS. STR_BASEMUSIC_NONE_DESCRIPTION :"Пустой" набор музыкального оформления, не содержащий никакой музыки. ##id 0x2000 diff --git a/src/lang/serbian.txt b/src/lang/serbian.txt index 05f89371ff..19973dbb98 100644 --- a/src/lang/serbian.txt +++ b/src/lang/serbian.txt @@ -3168,8 +3168,6 @@ STR_TOWN_POPULATION :{BLACK}Svetska STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Grad) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Populacija: {ORANGE}{COMMA}{BLACK} Zgrada: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Broj putnika tokom meseca: {ORANGE}{COMMA}{BLACK} najviše: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Količina pošte tokom meseca: {ORANGE}{COMMA}{BLACK} najviše: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Tovar potreban za razvoj naselja: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} potrebno STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} potrebno zimi diff --git a/src/lang/simplified_chinese.txt b/src/lang/simplified_chinese.txt index 3670314194..dfa999bab7 100644 --- a/src/lang/simplified_chinese.txt +++ b/src/lang/simplified_chinese.txt @@ -674,7 +674,9 @@ STR_PLAYLIST_TRACK_NAME :{TINY_FONT}{LTB STR_PLAYLIST_TRACK_INDEX :{TINY_FONT}{BLACK}全部音轨列表 STR_PLAYLIST_PROGRAM :{TINY_FONT}{BLACK}当前选用'{STRING}'列表 STR_PLAYLIST_CLEAR :{TINY_FONT}{BLACK}清除 +STR_PLAYLIST_CHANGE_SET :更改设置 STR_PLAYLIST_TOOLTIP_CLEAR_CURRENT_PROGRAM_CUSTOM1 :{BLACK}清除当前列表中曲目{}(仅限自定义1或自定义2) +STR_PLAYLIST_TOOLTIP_CHANGE_SET :{BLACK}选择另一种已安装的音乐 STR_PLAYLIST_TOOLTIP_CLICK_TO_ADD_TRACK :{BLACK}点击音乐曲目以加入当前播放列表{}(仅限自定义1或自定义2) STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK :{BLACK}点击音乐曲目以从当前播放列表中删除{}(仅限自定义1或自定义2) @@ -1334,6 +1336,7 @@ STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_HELPTEXT :设置缩略地 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_GREEN :绿色 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_DARK_GREEN :深绿色 STR_CONFIG_SETTING_SMALLMAP_LAND_COLOUR_VIOLET :紫色 +STR_CONFIG_SETTING_SCROLLMODE_RMB :鼠标右键移动地图 STR_CONFIG_SETTING_SMOOTH_SCROLLING :平滑视角滚动: {STRING} STR_CONFIG_SETTING_SMOOTH_SCROLLING_HELPTEXT :设置在缩略图上点击或者发出转到特定目标的命令时主视角的转换方式,如果“打开”本选项,视角平缓滚动,“关闭”时直接跳转到目标位置 STR_CONFIG_SETTING_MEASURE_TOOLTIP :建设时显示测量数据:{STRING} @@ -2974,8 +2977,6 @@ STR_TOWN_POPULATION :{BLACK}所有 STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (都市) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}人口:{ORANGE}{COMMA}{BLACK} 房屋:{ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}上月旅客数量:{ORANGE}{COMMA}{BLACK} 最大值:{ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}上月邮包数量:{ORANGE}{COMMA}{BLACK} 最大值:{ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}城镇发展所必需的货物: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{RED} 需要: {ORANGE}{STRING} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} 冬季的需求 @@ -3649,6 +3650,7 @@ STR_VEHICLE_INFO_AGE :{COMMA} 年 ({C STR_VEHICLE_INFO_AGE_RED :{RED}{COMMA} 年 ({COMMA}) STR_VEHICLE_INFO_MAX_SPEED :{BLACK}最大速度:{LTBLUE}{VELOCITY} +STR_VEHICLE_INFO_MAX_SPEED_TYPE :{BLACK}最高速度: {LTBLUE}{VELOCITY} {BLACK}飞机种类: {LTBLUE}{STRING} STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED :{BLACK}重量:{LTBLUE}{WEIGHT_SHORT} {BLACK}功率:{LTBLUE}{POWER}{BLACK} 最大速度:{LTBLUE}{VELOCITY} STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :{BLACK}重量:{LTBLUE}{WEIGHT_SHORT} {BLACK}功率:{LTBLUE}{POWER}{BLACK} 最大速度:{LTBLUE}{VELOCITY} {BLACK}最大牵引力:{LTBLUE}{FORCE} @@ -4466,6 +4468,7 @@ STR_BASESOUNDS_DOS_DESCRIPTION :运输大亨DOS STR_BASESOUNDS_WIN_DESCRIPTION :Transport Tycoon Deluxe Windows (运输大亨Windows豪华版)的原版音效包. STR_BASESOUNDS_NONE_DESCRIPTION :一个空的音效包. STR_BASEMUSIC_WIN_DESCRIPTION :Transport Tycoon Deluxe(运输大亨Windows豪华版)的原版音乐包 +STR_BASEMUSIC_DOS_DESCRIPTION :运输大亨DOS豪华版原版音乐。 STR_BASEMUSIC_NONE_DESCRIPTION :一个没有实际内容的音乐包. ##id 0x2000 diff --git a/src/lang/slovak.txt b/src/lang/slovak.txt index 4ee2e5b7d7..2b76b4bd34 100644 --- a/src/lang/slovak.txt +++ b/src/lang/slovak.txt @@ -3041,8 +3041,6 @@ STR_TOWN_POPULATION :{BLACK}Svetová STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Mesto) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Obyvateľstvo: {ORANGE}{COMMA}{BLACK} Domov: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Cestujúci posledný mesiac: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Pošta posledný mesiac: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Tovar potrebný k rozrastu mesta: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} potrebný STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} potrebné v zime diff --git a/src/lang/slovenian.txt b/src/lang/slovenian.txt index 156c1a69cd..c385b7f8e1 100644 --- a/src/lang/slovenian.txt +++ b/src/lang/slovenian.txt @@ -3127,8 +3127,6 @@ STR_TOWN_POPULATION :{BLACK}Svetovno STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Mesto) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Prebivalstvo: {ORANGE}{COMMA}{BLACK} Število stavb: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Potnikov prejšnji mesec: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Pošte prejšnji mesec: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Količina potrebnega tovora za rast mesta: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} potrebno STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} je potreben pozimi diff --git a/src/lang/spanish.txt b/src/lang/spanish.txt index 774205fa58..7dd3c4a548 100644 --- a/src/lang/spanish.txt +++ b/src/lang/spanish.txt @@ -2978,8 +2978,6 @@ STR_TOWN_POPULATION :{BLACK}Poblaci STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Ciudad) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Habitantes: {ORANGE}{COMMA}{BLACK} Casas: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Pasajeros último mes: {ORANGE}{COMMA}{BLACK} Máx.: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Correo último mes: {ORANGE}{COMMA}{BLACK} Máx.: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Carga necesaria para crecimiento del municipio: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} requeridos STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} requerido en invierno diff --git a/src/lang/spanish_MX.txt b/src/lang/spanish_MX.txt index fced7f7002..e8422b6aa4 100644 --- a/src/lang/spanish_MX.txt +++ b/src/lang/spanish_MX.txt @@ -2988,8 +2988,6 @@ STR_TOWN_POPULATION :{BLACK}Poblaci STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (ciudad) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Habitantes: {ORANGE}{COMMA}{BLACK} Casas: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Pasajeros mes pasado: {ORANGE}{COMMA}{BLACK} Máx.: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Correo mes pasado: {ORANGE}{COMMA}{BLACK} Máx.: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Cargamento necesario para crecimiento: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} requeridos STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} requerido en invierno diff --git a/src/lang/swedish.txt b/src/lang/swedish.txt index 3830a3bf22..ca554caca1 100644 --- a/src/lang/swedish.txt +++ b/src/lang/swedish.txt @@ -2977,8 +2977,6 @@ STR_TOWN_POPULATION :{BLACK}Global f STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Stad) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Invånare: {ORANGE}{COMMA}{BLACK} Hus: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passagerare förra månaden: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Post förra månaden: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Fraktgods behövs för ortens tillväxt: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} krävs STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} krävs under vintern diff --git a/src/lang/tamil.txt b/src/lang/tamil.txt index 629cf147cd..6dbcb3e68d 100644 --- a/src/lang/tamil.txt +++ b/src/lang/tamil.txt @@ -2620,8 +2620,6 @@ STR_TOWN_POPULATION :{BLACK}உல STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (மாநகரம்) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}மக்கள்தொகை: {ORANGE}{COMMA}{BLACK} வீடுகள்: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}சென்ற மாத பயணிகள்: {ORANGE}{COMMA}{BLACK} அதிகம்: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}சென்ற மாத அஞ்சல்கள்: {ORANGE}{COMMA}{BLACK} அதிகம்: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}நகரத்தின் வளர்ச்சியிற்கு தேவையான சரக்குகள்: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} தேவை STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} குளிர்காலத்தில் தேவை diff --git a/src/lang/thai.txt b/src/lang/thai.txt index 5b7618cf47..4661c8ca09 100644 --- a/src/lang/thai.txt +++ b/src/lang/thai.txt @@ -2905,8 +2905,6 @@ STR_TOWN_POPULATION :{BLACK}ปร STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}นคร {TOWN} STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}จำนวนประชากร: {ORANGE}{COMMA}{BLACK} จำนวนอาคาร: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}ผู้โดยสารเมื่อเดือนที่แล้ว: {ORANGE}{COMMA}{BLACK} จำนวนสูงสุด: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}ไปรษณีย์ภัณฑ์เมื่อเดือนที่แล้ว: {ORANGE}{COMMA}{BLACK} จำนวนสูงสุด: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}ความต้องการสินค้าสำหรับการขยายตัวของเมือง: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} required STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} ต้องการหิมะ diff --git a/src/lang/traditional_chinese.txt b/src/lang/traditional_chinese.txt index 668bcbc5d6..f2676f1ebd 100644 --- a/src/lang/traditional_chinese.txt +++ b/src/lang/traditional_chinese.txt @@ -2973,8 +2973,6 @@ STR_TOWN_POPULATION :{BLACK}世界 STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (城市) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}人口:{ORANGE}{COMMA}{BLACK} 房屋:{ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}上月乘客數字:{ORANGE}{COMMA}{BLACK} 紀錄最高:{ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}上月郵件數量:{ORANGE}{COMMA}{BLACK} 紀錄最高:{ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}市鎮成長所需貨物: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{RED}需要 {ORANGE}{STRING} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} 必須是冬天 diff --git a/src/lang/turkish.txt b/src/lang/turkish.txt index 7da24aabf8..9d4bb12860 100644 --- a/src/lang/turkish.txt +++ b/src/lang/turkish.txt @@ -2978,8 +2978,6 @@ STR_TOWN_POPULATION :{BLACK}Dünya n STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Şehir) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Nüfus: {ORANGE}{COMMA}{BLACK} Ev: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Geçen ayki yolcu: {ORANGE}{COMMA}{BLACK} azami: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Geçen ayki posta: {ORANGE}{COMMA}{BLACK} azami: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Kasaba büyümesi için gerekli kargo: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} gerekli STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} kışın gerekir diff --git a/src/lang/ukrainian.txt b/src/lang/ukrainian.txt index ae23169f37..932d0462b8 100644 --- a/src/lang/ukrainian.txt +++ b/src/lang/ukrainian.txt @@ -3104,8 +3104,6 @@ STR_TOWN_POPULATION :{BLACK}Насе STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (місто) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Населення: {ORANGE}{COMMA}{BLACK} Будинки: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Пасажирів за місяць: {ORANGE}{COMMA}{BLACK} найбільше: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Пошти за місяць: {ORANGE}{COMMA}{BLACK} найбільше: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Вантаж, потрібний для зростання міста: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} потрібно STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} потрібно взимку diff --git a/src/lang/unfinished/frisian.txt b/src/lang/unfinished/frisian.txt index c82a96a197..7ee25160cb 100644 --- a/src/lang/unfinished/frisian.txt +++ b/src/lang/unfinished/frisian.txt @@ -2747,8 +2747,6 @@ STR_TOWN_POPULATION :{BLACK}Wrâldpo STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (City) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Ynwenners: {ORANGE}{COMMA}{BLACK} Hûzen: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Passazjiers lêste moanne: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Post lêste moanne: {ORANGE}{COMMA}{BLACK} maks: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Guod nedich foar stêdsgroei: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} ferplichte STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} ferplichte yn de winter diff --git a/src/lang/unfinished/persian.txt b/src/lang/unfinished/persian.txt index c70cbefb9e..908be68e0a 100644 --- a/src/lang/unfinished/persian.txt +++ b/src/lang/unfinished/persian.txt @@ -2658,8 +2658,6 @@ STR_TOWN_POPULATION :{BLACK}جمعی STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (شهر) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}جمعیت: {ORANGE}{COMMA}{BLACK} خانه ها: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}مسافران در ماه گذشته: {ORANGE}{COMMA}{BLACK} حداکثر: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}نامه ها در ماه گذشته: {ORANGE}{COMMA}{BLACK} حداکثر: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}نوع بار برای رشد شهر: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} ضروری STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} در زمستان ضروری است diff --git a/src/lang/vietnamese.txt b/src/lang/vietnamese.txt index bf7628d765..7f20bb0134 100644 --- a/src/lang/vietnamese.txt +++ b/src/lang/vietnamese.txt @@ -2977,8 +2977,6 @@ STR_TOWN_POPULATION :{BLACK}Dân s STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Thành Phố) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Dân số: {ORANGE}{COMMA}{BLACK} Toà nhà: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Du khách tháng trước: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Bưu kiện tháng trước: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Hàng hoá cần để đô thị tăng trưởng: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} được yêu cầu STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} Yêu cầu trong mùa đông diff --git a/src/lang/welsh.txt b/src/lang/welsh.txt index 728bd638c9..0a076afe4d 100644 --- a/src/lang/welsh.txt +++ b/src/lang/welsh.txt @@ -2977,8 +2977,6 @@ STR_TOWN_POPULATION :{BLACK}Poblogae STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (Dinas) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Poblogaeth: {ORANGE}{COMMA}{BLACK} Tai: {ORANGE}{COMMA} -STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX :{BLACK}Teithwyr mis diwethaf: {ORANGE}{COMMA}{BLACK} uchafswm: {ORANGE}{COMMA} -STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX :{BLACK}Post mis diwethaf: {ORANGE}{COMMA}{BLACK} uchafswm: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Nwyddau angenrheidiol ar gyfer tyfiant y dref: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{RED}Angen {ORANGE}{STRING} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} ei angen yn y gaeaf diff --git a/src/linkgraph/linkgraph_gui.cpp b/src/linkgraph/linkgraph_gui.cpp index 015dc52ffa..c91fa20588 100644 --- a/src/linkgraph/linkgraph_gui.cpp +++ b/src/linkgraph/linkgraph_gui.cpp @@ -319,7 +319,7 @@ void LinkGraphOverlay::SetCompanyMask(uint32 company_mask) /** Make a number of rows with buttons for each company for the linkgraph legend window. */ NWidgetBase *MakeCompanyButtonRowsLinkGraphGUI(int *biggest_index) { - return MakeCompanyButtonRows(biggest_index, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST, 3, STR_LINKGRAPH_LEGEND_SELECT_COMPANIES); + return MakeCompanyButtonRows(biggest_index, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST, 3, STR_NULL); } NWidgetBase *MakeSaturationLegendLinkGraphGUI(int *biggest_index) @@ -496,10 +496,48 @@ void LinkGraphLegendWindow::DrawWidget(const Rect &r, int widget) const if (this->IsWidgetDisabled(widget)) return; CargoSpec *cargo = CargoSpec::Get(widget - WID_LGL_CARGO_FIRST); GfxFillRect(r.left + 2, r.top + 2, r.right - 2, r.bottom - 2, cargo->legend_colour); - DrawString(r.left, r.right, (r.top + r.bottom + 1 - FONT_HEIGHT_SMALL) / 2, cargo->abbrev, TC_BLACK, SA_HOR_CENTER); + DrawString(r.left, r.right, (r.top + r.bottom + 1 - FONT_HEIGHT_SMALL) / 2, cargo->abbrev, GetContrastColour(cargo->legend_colour, 73), SA_HOR_CENTER); } } +bool LinkGraphLegendWindow::OnHoverCommon(Point pt, int widget, TooltipCloseCondition close_cond) +{ + if (IsInsideMM(widget, WID_LGL_COMPANY_FIRST, WID_LGL_COMPANY_LAST + 1)) { + if (this->IsWidgetDisabled(widget)) { + GuiShowTooltips(this, STR_LINKGRAPH_LEGEND_SELECT_COMPANIES, 0, NULL, close_cond); + } else { + uint64 params[2]; + CompanyID cid = (CompanyID)(widget - WID_LGL_COMPANY_FIRST); + params[0] = STR_LINKGRAPH_LEGEND_SELECT_COMPANIES; + params[1] = cid; + GuiShowTooltips(this, STR_LINKGRAPH_LEGEND_COMPANY_TOOLTIP, 2, params, close_cond); + } + return true; + } + if (IsInsideMM(widget, WID_LGL_CARGO_FIRST, WID_LGL_CARGO_LAST + 1)) { + if (this->IsWidgetDisabled(widget)) return false; + CargoSpec *cargo = CargoSpec::Get(widget - WID_LGL_CARGO_FIRST); + uint64 params[1]; + params[0] = cargo->name; + GuiShowTooltips(this, STR_BLACK_STRING, 1, params, close_cond); + return true; + } + return false; +} + +void LinkGraphLegendWindow::OnHover(Point pt, int widget) +{ + this->OnHoverCommon(pt, widget, TCC_HOVER); +} + +bool LinkGraphLegendWindow::OnRightClick(Point pt, int widget) +{ + if (_settings_client.gui.hover_delay_ms == 0) { + return this->OnHoverCommon(pt, widget, TCC_RIGHT_CLICK); + } + return false; +} + /** * Update the overlay with the new company selection. */ diff --git a/src/linkgraph/linkgraph_gui.h b/src/linkgraph/linkgraph_gui.h index 12f1f6e736..a933bfc683 100644 --- a/src/linkgraph/linkgraph_gui.h +++ b/src/linkgraph/linkgraph_gui.h @@ -15,6 +15,7 @@ #include "../company_func.h" #include "../station_base.h" #include "../widget_type.h" +#include "../window_gui.h" #include "linkgraph_base.h" #include #include @@ -101,6 +102,8 @@ public: virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize); virtual void DrawWidget(const Rect &r, int widget) const; + virtual void OnHover(Point pt, int widget); + virtual bool OnRightClick(Point pt, int widget); virtual void OnClick(Point pt, int widget, int click_count); virtual void OnInvalidateData(int data = 0, bool gui_scope = true); @@ -109,6 +112,7 @@ private: void UpdateOverlayCompanies(); void UpdateOverlayCargoes(); + bool OnHoverCommon(Point pt, int widget, TooltipCloseCondition close_cond); }; #endif /* LINKGRAPH_GUI_H */ diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 01e8d2c45d..dd75410628 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -313,10 +313,7 @@ public: for (CargoID i = 0; i < NUM_CARGO; ++i) { if (acceptance[i] > 0) { /* Add a comma between each item. */ - if (found) { - *strp++ = ','; - *strp++ = ' '; - } + if (found) strp = strecpy(strp, ", ", lastof(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])); found = true; /* If the accepted value is less than 8, show it in 1/8:ths */ @@ -659,7 +656,7 @@ struct TooltipsWindow : public Window this->string_id = str; assert_compile(sizeof(this->params[0]) == sizeof(params[0])); assert(paramcount <= lengthof(this->params)); - memcpy(this->params, params, sizeof(this->params[0]) * paramcount); + if (paramcount > 0) memcpy(this->params, params, sizeof(this->params[0]) * paramcount); this->paramcount = paramcount; this->close_cond = close_tooltip; diff --git a/src/music.cpp b/src/music.cpp index 4001e621e1..3d0e40bf9b 100644 --- a/src/music.cpp +++ b/src/music.cpp @@ -11,11 +11,69 @@ #include "stdafx.h" + /** The type of set we're replacing */ #define SET_TYPE "music" #include "base_media_func.h" #include "safeguards.h" +#include "fios.h" + + +/** + * Read the name of a music CAT file entry. + * @param filename Name of CAT file to read from + * @param entrynum Index of entry whose name to read + * @return Pointer to string, caller is responsible for freeing memory, + * NULL if entrynum does not exist. + */ +char *GetMusicCatEntryName(const char *filename, size_t entrynum) +{ + if (!FioCheckFileExists(filename, BASESET_DIR)) return NULL; + + FioOpenFile(CONFIG_SLOT, filename, BASESET_DIR); + uint32 ofs = FioReadDword(); + size_t entry_count = ofs / 8; + if (entrynum < entry_count) { + FioSeekTo(entrynum * 8, SEEK_SET); + FioSeekTo(FioReadDword(), SEEK_SET); + byte namelen = FioReadByte(); + char *name = MallocT(namelen + 1); + FioReadBlock(name, namelen); + name[namelen] = '\0'; + return name; + } + return NULL; +} + +/** + * Read the full data of a music CAT file entry. + * @param filename Name of CAT file to read from. + * @param entrynum Index of entry to read + * @param[out] entrylen Receives length of data read + * @return Pointer to buffer with data read, caller is responsible for freeind memory, + * NULL if entrynum does not exist. + */ +byte *GetMusicCatEntryData(const char *filename, size_t entrynum, size_t &entrylen) +{ + entrylen = 0; + if (!FioCheckFileExists(filename, BASESET_DIR)) return NULL; + + FioOpenFile(CONFIG_SLOT, filename, BASESET_DIR); + uint32 ofs = FioReadDword(); + size_t entry_count = ofs / 8; + if (entrynum < entry_count) { + FioSeekTo(entrynum * 8, SEEK_SET); + size_t entrypos = FioReadDword(); + entrylen = FioReadDword(); + FioSeekTo(entrypos, SEEK_SET); + FioSkipBytes(FioReadByte()); + byte *data = MallocT(entrylen); + FioReadBlock(data, entrylen); + return data; + } + return NULL; +} INSTANTIATE_BASE_MEDIA_METHODS(BaseMedia, MusicSet) @@ -66,34 +124,73 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f if (ret) { this->num_available = 0; IniGroup *names = ini->GetGroup("names"); - for (uint i = 0, j = 1; i < lengthof(this->song_name); i++) { + IniGroup *catindex = ini->GetGroup("catindex"); + IniGroup *timingtrim = ini->GetGroup("timingtrim"); + uint tracknr = 1; + for (uint i = 0; i < lengthof(this->songinfo); i++) { const char *filename = this->files[i].filename; - if (names == NULL || StrEmpty(filename)) { - this->song_name[i][0] = '\0'; + if (names == NULL || StrEmpty(filename) || this->files[i].check_result == MD5File::CR_NO_FILE) { + this->songinfo[i].songname[0] = '\0'; continue; } - IniItem *item = NULL; + this->songinfo[i].filename = filename; // non-owned pointer + + IniItem *item = catindex->GetItem(_music_file_names[i], false); + if (item != NULL && !StrEmpty(item->value)) { + /* Song has a CAT file index, assume it's MPS MIDI format */ + this->songinfo[i].filetype = MTT_MPSMIDI; + this->songinfo[i].cat_index = atoi(item->value); + char *songname = GetMusicCatEntryName(filename, this->songinfo[i].cat_index); + if (songname == NULL) { + DEBUG(grf, 0, "Base music set song missing from CAT file: %s/%d", filename, this->songinfo[i].cat_index); + this->songinfo[i].songname[0] = '\0'; + continue; + } + strecpy(this->songinfo[i].songname, songname, lastof(this->songinfo[i].songname)); + free(songname); + } else { + this->songinfo[i].filetype = MTT_STANDARDMIDI; + } + + const char *trimmed_filename = filename; /* As we possibly add a path to the filename and we compare * on the filename with the path as in the .obm, we need to * keep stripping path elements until we find a match. */ - for (const char *p = filename; p != NULL; p = strchr(p, PATHSEPCHAR)) { + for (; trimmed_filename != NULL; trimmed_filename = strchr(trimmed_filename, PATHSEPCHAR)) { /* Remove possible double path separator characters from * the beginning, so we don't start reading e.g. root. */ - while (*p == PATHSEPCHAR) p++; + while (*trimmed_filename == PATHSEPCHAR) trimmed_filename++; - item = names->GetItem(p, false); + item = names->GetItem(trimmed_filename, false); if (item != NULL && !StrEmpty(item->value)) break; } - if (item == NULL || StrEmpty(item->value)) { - DEBUG(grf, 0, "Base music set song name missing: %s", filename); - return false; + if (this->songinfo[i].filetype == MTT_STANDARDMIDI) { + if (item != NULL && !StrEmpty(item->value)) { + strecpy(this->songinfo[i].songname, item->value, lastof(this->songinfo[i].songname)); + } else { + DEBUG(grf, 0, "Base music set song name missing: %s", filename); + return false; + } + } + this->num_available++; + + /* Number the theme song (if any) track 0, rest are normal */ + if (i == 0) { + this->songinfo[i].tracknr = 0; + } else { + this->songinfo[i].tracknr = tracknr++; } - strecpy(this->song_name[i], item->value, lastof(this->song_name[i])); - this->track_nr[i] = j++; - this->num_available++; + item = timingtrim->GetItem(trimmed_filename, false); + if (item != NULL && !StrEmpty(item->value)) { + const char *endpos = strchr(item->value, ':'); + if (endpos != NULL) { + this->songinfo[i].override_start = atoi(item->value); + this->songinfo[i].override_end = atoi(endpos + 1); + } + } } } return ret; diff --git a/src/music/allegro_m.cpp b/src/music/allegro_m.cpp index 77b488186a..906aec84fc 100644 --- a/src/music/allegro_m.cpp +++ b/src/music/allegro_m.cpp @@ -14,6 +14,7 @@ #include "../stdafx.h" #include "../debug.h" #include "allegro_m.h" +#include "midifile.hpp" #include #include "../safeguards.h" @@ -58,11 +59,17 @@ void MusicDriver_Allegro::Stop() if (--_allegro_instance_count == 0) allegro_exit(); } -void MusicDriver_Allegro::PlaySong(const char *filename) +void MusicDriver_Allegro::PlaySong(const MusicSongInfo &song) { + std::string filename = MidiFile::GetSMFFile(song); + if (_midi != NULL) destroy_midi(_midi); - _midi = load_midi(filename); - play_midi(_midi, false); + if (!filename.empty()) { + _midi = load_midi(filename.c_str()); + play_midi(_midi, false); + } else { + _midi = NULL; + } } void MusicDriver_Allegro::StopSong() diff --git a/src/music/allegro_m.h b/src/music/allegro_m.h index 69cf59569a..65d8ab811d 100644 --- a/src/music/allegro_m.h +++ b/src/music/allegro_m.h @@ -21,7 +21,7 @@ public: /* virtual */ void Stop(); - /* virtual */ void PlaySong(const char *filename); + /* virtual */ void PlaySong(const MusicSongInfo &song); /* virtual */ void StopSong(); diff --git a/src/music/bemidi.cpp b/src/music/bemidi.cpp index 2bc2074763..ff56d787f1 100644 --- a/src/music/bemidi.cpp +++ b/src/music/bemidi.cpp @@ -12,6 +12,8 @@ #include "../stdafx.h" #include "../openttd.h" #include "bemidi.h" +#include "../base_media_base.h" +#include "midifile.hpp" /* BeOS System Includes */ #include @@ -34,13 +36,17 @@ void MusicDriver_BeMidi::Stop() midiSynthFile.UnloadFile(); } -void MusicDriver_BeMidi::PlaySong(const char *filename) +void MusicDriver_BeMidi::PlaySong(const MusicSongInfo &song) { + std::string filename = MidiFile::GetSMFFile(song); + this->Stop(); - entry_ref midiRef; - get_ref_for_path(filename, &midiRef); - midiSynthFile.LoadFile(&midiRef); - midiSynthFile.Start(); + if (!filename.empty()) { + entry_ref midiRef; + get_ref_for_path(filename.c_str(), &midiRef); + midiSynthFile.LoadFile(&midiRef); + midiSynthFile.Start(); + } } void MusicDriver_BeMidi::StopSong() diff --git a/src/music/bemidi.h b/src/music/bemidi.h index 23c6249d59..7c546525d2 100644 --- a/src/music/bemidi.h +++ b/src/music/bemidi.h @@ -21,7 +21,7 @@ public: /* virtual */ void Stop(); - /* virtual */ void PlaySong(const char *filename); + /* virtual */ void PlaySong(const MusicSongInfo &song); /* virtual */ void StopSong(); diff --git a/src/music/cocoa_m.cpp b/src/music/cocoa_m.cpp index 925dc21ab5..8d97aedf06 100644 --- a/src/music/cocoa_m.cpp +++ b/src/music/cocoa_m.cpp @@ -18,7 +18,9 @@ #include "../stdafx.h" #include "../os/macosx/macos.h" #include "cocoa_m.h" +#include "midifile.hpp" #include "../debug.h" +#include "../base_media_base.h" #define Rect OTTDRect #define Point OTTDPoint @@ -141,11 +143,13 @@ void MusicDriver_Cocoa::Stop() /** * Starts playing a new song. * - * @param filename Path to a MIDI file. + * @param song Description of music to load and play */ -void MusicDriver_Cocoa::PlaySong(const char *filename) +void MusicDriver_Cocoa::PlaySong(const MusicSongInfo &song) { - DEBUG(driver, 2, "cocoa_m: trying to play '%s'", filename); + std::string filename = MidiFile::GetSMFFile(song); + + DEBUG(driver, 2, "cocoa_m: trying to play '%s'", filename.c_str()); this->StopSong(); if (_sequence != NULL) { @@ -153,12 +157,14 @@ void MusicDriver_Cocoa::PlaySong(const char *filename) _sequence = NULL; } + if (filename.empty()) return; + if (NewMusicSequence(&_sequence) != noErr) { DEBUG(driver, 0, "cocoa_m: Failed to create music sequence"); return; } - const char *os_file = OTTD2FS(filename); + const char *os_file = OTTD2FS(filename.c_str()); CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)os_file, strlen(os_file), false); #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) @@ -218,7 +224,7 @@ void MusicDriver_Cocoa::PlaySong(const char *filename) if (MusicPlayerStart(_player) != noErr) return; _playing = true; - DEBUG(driver, 3, "cocoa_m: playing '%s'", filename); + DEBUG(driver, 3, "cocoa_m: playing '%s'", filename.c_str()); } diff --git a/src/music/cocoa_m.h b/src/music/cocoa_m.h index 1963bef5b7..fdb10b84e6 100644 --- a/src/music/cocoa_m.h +++ b/src/music/cocoa_m.h @@ -20,7 +20,7 @@ public: /* virtual */ void Stop(); - /* virtual */ void PlaySong(const char *filename); + /* virtual */ void PlaySong(const MusicSongInfo &song); /* virtual */ void StopSong(); diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp index 934bb9fe26..d48f51e85f 100644 --- a/src/music/dmusic.cpp +++ b/src/music/dmusic.cpp @@ -21,6 +21,7 @@ #include "../core/mem_func.hpp" #include "../thread/thread.h" #include "../fileio_func.h" +#include "../base_media_base.h" #include "dmusic.h" #include "midifile.hpp" #include "midi.h" @@ -692,6 +693,9 @@ static void MidiThreadProc(void *) current_segment.start_block = bl; break; } else { + /* Skip the transmission delay compensation performed in the Win32 MIDI driver. + * The DMusic driver will most likely be used with the MS softsynth, which is not subject to transmission delays. + */ DEBUG(driver, 2, "DMusic: timer: start from block %d (ticktime %d, realtime %.3f, bytes %d)", (int)bl, (int)block.ticktime, ((int)block.realtime) / 1000.0, (int)preload_bytes); playback_start_time -= block.realtime * MIDITIME_TO_REFTIME; break; @@ -722,14 +726,6 @@ static void MidiThreadProc(void *) while (current_block < current_file.blocks.size()) { MidiFile::DataBlock &block = current_file.blocks[current_block]; - /* check that block is not in the future */ - REFERENCE_TIME playback_time = current_time - playback_start_time; - if (block.realtime * MIDITIME_TO_REFTIME > playback_time + 3 *_playback.preload_time * MS_TO_REFTIME) { - /* Stop the thread loop until we are at the preload time of the next block. */ - next_timeout = Clamp(((int64)block.realtime * MIDITIME_TO_REFTIME - playback_time) / MS_TO_REFTIME - _playback.preload_time, 0, 1000); - DEBUG(driver, 9, "DMusic thread: Next event in %u ms (music %u, ref %lld)", next_timeout, block.realtime * MIDITIME_TO_REFTIME, playback_time); - break; - } /* check that block isn't at end-of-song override */ if (current_segment.end > 0 && block.ticktime >= current_segment.end) { if (current_segment.loop) { @@ -742,6 +738,14 @@ static void MidiThreadProc(void *) next_timeout = 0; break; } + /* check that block is not in the future */ + REFERENCE_TIME playback_time = current_time - playback_start_time; + if (block.realtime * MIDITIME_TO_REFTIME > playback_time + 3 *_playback.preload_time * MS_TO_REFTIME) { + /* Stop the thread loop until we are at the preload time of the next block. */ + next_timeout = Clamp(((int64)block.realtime * MIDITIME_TO_REFTIME - playback_time) / MS_TO_REFTIME - _playback.preload_time, 0, 1000); + DEBUG(driver, 9, "DMusic thread: Next event in %u ms (music %u, ref %lld)", next_timeout, block.realtime * MIDITIME_TO_REFTIME, playback_time); + break; + } /* Timestamp of the current block. */ block_time = playback_start_time + block.realtime * MIDITIME_TO_REFTIME; @@ -828,8 +832,8 @@ static void MidiThreadProc(void *) /* end? */ if (current_block == current_file.blocks.size()) { if (current_segment.loop) { - current_block = 0; - clock->GetTime(&playback_start_time); + current_block = current_segment.start_block; + playback_start_time = block_time - current_file.blocks[current_block].realtime * MIDITIME_TO_REFTIME; } else { _playback.do_stop = true; } @@ -1225,14 +1229,15 @@ void MusicDriver_DMusic::Stop() } -void MusicDriver_DMusic::PlaySong(const char *filename) +void MusicDriver_DMusic::PlaySong(const MusicSongInfo &song) { ThreadMutexLocker lock(_thread_mutex); - _playback.next_file.LoadFile(filename); - _playback.next_segment.start = 0; - _playback.next_segment.end = 0; - _playback.next_segment.loop = false; + if (!_playback.next_file.LoadSong(song)) return; + + _playback.next_segment.start = song.override_start; + _playback.next_segment.end = song.override_end; + _playback.next_segment.loop = song.loop; _playback.do_start = true; SetEvent(_thread_event); diff --git a/src/music/dmusic.h b/src/music/dmusic.h index 7287623e48..527e064e49 100644 --- a/src/music/dmusic.h +++ b/src/music/dmusic.h @@ -23,7 +23,7 @@ public: /* virtual */ void Stop(); - /* virtual */ void PlaySong(const char *filename); + /* virtual */ void PlaySong(const MusicSongInfo &song); /* virtual */ void StopSong(); diff --git a/src/music/extmidi.cpp b/src/music/extmidi.cpp index d39a050f6c..9d07761b73 100644 --- a/src/music/extmidi.cpp +++ b/src/music/extmidi.cpp @@ -17,6 +17,8 @@ #include "../video/video_driver.hpp" #include "../gfx_func.h" #include "extmidi.h" +#include "../base_media_base.h" +#include "midifile.hpp" #include #include #include @@ -83,10 +85,13 @@ void MusicDriver_ExtMidi::Stop() this->DoStop(); } -void MusicDriver_ExtMidi::PlaySong(const char *filename) +void MusicDriver_ExtMidi::PlaySong(const MusicSongInfo &song) { - strecpy(this->song, filename, lastof(this->song)); - this->DoStop(); + std::string filename = MidiFile::GetSMFFile(song); + if (!filename.empty()) { + strecpy(this->song, filename.c_str(), lastof(this->song)); + this->DoStop(); + } } void MusicDriver_ExtMidi::StopSong() diff --git a/src/music/extmidi.h b/src/music/extmidi.h index cfbd894596..e174dc9b08 100644 --- a/src/music/extmidi.h +++ b/src/music/extmidi.h @@ -28,7 +28,7 @@ public: /* virtual */ void Stop(); - /* virtual */ void PlaySong(const char *filename); + /* virtual */ void PlaySong(const MusicSongInfo &song); /* virtual */ void StopSong(); diff --git a/src/music/libtimidity.cpp b/src/music/libtimidity.cpp index 93284bd834..42c1e3c155 100644 --- a/src/music/libtimidity.cpp +++ b/src/music/libtimidity.cpp @@ -14,6 +14,8 @@ #include "../sound_type.h" #include "../debug.h" #include "libtimidity.h" +#include "midifile.hpp" +#include "../base_media_base.h" #include #include #include @@ -73,11 +75,14 @@ void MusicDriver_LibTimidity::Stop() mid_exit(); } -void MusicDriver_LibTimidity::PlaySong(const char *filename) +void MusicDriver_LibTimidity::PlaySong(const MusicSongInfo &song) { - this->StopSong(); + std::string filename = MidiFile::GetSMFFile(song); - _midi.stream = mid_istream_open_file(filename); + this->StopSong(); + if (filename.empty()) return; + + _midi.stream = mid_istream_open_file(filename.c_str()); if (_midi.stream == NULL) { DEBUG(driver, 0, "Could not open music file"); return; diff --git a/src/music/libtimidity.h b/src/music/libtimidity.h index abe17e7703..badb05bab2 100644 --- a/src/music/libtimidity.h +++ b/src/music/libtimidity.h @@ -21,7 +21,7 @@ public: /* virtual */ void Stop(); - /* virtual */ void PlaySong(const char *filename); + /* virtual */ void PlaySong(const MusicSongInfo &song); /* virtual */ void StopSong(); diff --git a/src/music/midifile.cpp b/src/music/midifile.cpp index eb7e02303e..688538db9b 100644 --- a/src/music/midifile.cpp +++ b/src/music/midifile.cpp @@ -12,13 +12,20 @@ #include "midifile.hpp" #include "../fileio_func.h" #include "../fileio_type.h" +#include "../string_func.h" #include "../core/endian_func.hpp" +#include "../base_media_base.h" #include "midi.h" #include +#include "../console_func.h" +#include "../console_internal.h" -/* implementation based on description at: http://www.somascape.org/midi/tech/mfile.html */ +/* SMF reader based on description at: http://www.somascape.org/midi/tech/mfile.html */ + + +static MidiFile *_midifile_instance = NULL; /** * Owning byte buffer readable as a stream. @@ -158,7 +165,7 @@ static bool ReadTrackChunk(FILE *file, MidiFile &target) return false; } - /* read chunk length and then the whole chunk */ + /* Read chunk length and then the whole chunk */ uint32 chunk_length; if (fread(&chunk_length, 1, 4, file) != 4) { return false; @@ -176,7 +183,7 @@ static bool ReadTrackChunk(FILE *file, MidiFile &target) byte last_status = 0; bool running_sysex = false; while (!chunk.IsEnd()) { - /* read deltatime for event, start new block */ + /* Read deltatime for event, start new block */ uint32 deltatime = 0; if (!chunk.ReadVariableLength(deltatime)) { return false; @@ -186,14 +193,14 @@ static bool ReadTrackChunk(FILE *file, MidiFile &target) block = &target.blocks.back(); } - /* read status byte */ + /* Read status byte */ byte status; if (!chunk.ReadByte(status)) { return false; } if ((status & 0x80) == 0) { - /* high bit not set means running status message, status is same as last + /* High bit not set means running status message, status is same as last * convert to explicit status */ chunk.Rewind(1); status = last_status; @@ -266,7 +273,7 @@ static bool ReadTrackChunk(FILE *file, MidiFile &target) return false; } if (data[length] != 0xF7) { - /* engage Casio weirdo mode - convert to normal sysex */ + /* Engage Casio weirdo mode - convert to normal sysex */ running_sysex = true; *block->data.Append() = 0xF7; } else { @@ -312,18 +319,20 @@ static bool FixupMidiData(MidiFile &target) std::sort(target.blocks.begin(), target.blocks.end(), TicktimeAscending); if (target.tempos.size() == 0) { - /* no tempo information, assume 120 bpm (500,000 microseconds per beat */ + /* No tempo information, assume 120 bpm (500,000 microseconds per beat */ target.tempos.push_back(MidiFile::TempoChange(0, 500000)); } - /* add sentinel tempo at end */ + /* Add sentinel tempo at end */ target.tempos.push_back(MidiFile::TempoChange(UINT32_MAX, 0)); - /* merge blocks with identical tick times */ + /* Merge blocks with identical tick times */ std::vector merged_blocks; uint32 last_ticktime = 0; for (size_t i = 0; i < target.blocks.size(); i++) { MidiFile::DataBlock &block = target.blocks[i]; - if (block.ticktime > last_ticktime || merged_blocks.size() == 0) { + if (block.data.Length() == 0) { + continue; + } else if (block.ticktime > last_ticktime || merged_blocks.size() == 0) { merged_blocks.push_back(block); last_ticktime = block.ticktime; } else { @@ -333,7 +342,7 @@ static bool FixupMidiData(MidiFile &target) } std::swap(merged_blocks, target.blocks); - /* annotate blocks with real time */ + /* Annotate blocks with real time */ last_ticktime = 0; uint32 last_realtime = 0; size_t cur_tempo = 0, cur_block = 0; @@ -390,13 +399,13 @@ bool MidiFile::ReadSMFHeader(FILE *file, SMFHeader &header) return false; } - /* check magic, 'MThd' followed by 4 byte length indicator (always = 6 in SMF) */ + /* Check magic, 'MThd' followed by 4 byte length indicator (always = 6 in SMF) */ const byte magic[] = { 'M', 'T', 'h', 'd', 0x00, 0x00, 0x00, 0x06 }; if (MemCmpT(buffer, magic, sizeof(magic)) != 0) { return false; } - /* read the parameters of the file */ + /* Read the parameters of the file */ header.format = (buffer[8] << 8) | buffer[9]; header.tracks = (buffer[10] << 8) | buffer[11]; header.tickdiv = (buffer[12] << 8) | buffer[13]; @@ -410,12 +419,15 @@ bool MidiFile::ReadSMFHeader(FILE *file, SMFHeader &header) */ bool MidiFile::LoadFile(const char *filename) { + _midifile_instance = this; + this->blocks.clear(); this->tempos.clear(); this->tickdiv = 0; bool success = false; FILE *file = FioFOpenFile(filename, "rb", Subdirectory::BASESET_DIR); + if (file == NULL) return false; SMFHeader header; if (!ReadSMFHeader(file, header)) goto cleanup; @@ -440,6 +452,383 @@ cleanup: return success; } + +/** + * Decoder for "MPS MIDI" format data. + * This format for MIDI music is also used in a few other Microprose games contemporary with Transport Tycoon. + * + * The song data are usually packed inside a CAT file, with one CAT chunk per song. The song titles are used as names for the CAT chunks. + * + * Unlike the Standard MIDI File format, which is based on the IFF structure, the MPS MIDI format is best described as two linked lists of sub-tracks, + * the first list contains a number of reusable "segments", and the second list contains the "master tracks". Each list is prefixed with a byte + * giving the number of elements in the list, and the actual list is just a byte count (BE16 format) for the segment/track followed by the actual data, + * there is no index as such, so the entire data must be seeked through to build an index. + * + * The actual MIDI data inside each track is almost standard MIDI, prefixing every event with a delay, encoded using the same variable-length format + * used in SMF. A few status codes have changed meaning in MPS MIDI: 0xFE changes control from master track to a segment, 0xFD returns from a segment + * to the master track, and 0xFF is used to end the song. (In Standard MIDI all those values must only occur in real-time data.) + * + * As implemented in the original decoder, there is no support for recursively calling segments from segments, i.e. code 0xFE must only occur in + * a master track, and code 0xFD must only occur in a segment. There are no checks made for this, it's assumed that the only input data will ever + * be the original game music, not music from other games, or new productions. + * + * Additionally, some program change and controller events are given special meaning, see comments in the code. + */ +struct MpsMachine { + /** Starting parameter and playback status for one channel/track */ + struct Channel { + byte cur_program; ///< program selected, used for velocity scaling (lookup into programvelocities array) + byte running_status; ///< last midi status code seen + uint16 delay; ///< frames until next command + uint32 playpos; ///< next byte to play this channel from + uint32 startpos; ///< start position of master track + uint32 returnpos; ///< next return position after playing a segment + Channel() : cur_program(0xFF), running_status(0), delay(0), playpos(0), startpos(0), returnpos(0) { } + }; + Channel channels[16]; ///< playback status for each MIDI channel + std::vector segments; ///< pointers into songdata to repeatable data segments + int16 tempo_ticks; ///< ticker that increments when playing a frame, decrements before playing a frame + int16 current_tempo; ///< threshold for actually playing a frame + int16 initial_tempo; ///< starting tempo of song + bool shouldplayflag; ///< not-end-of-song flag + + static const int TEMPO_RATE; + static const byte programvelocities[128]; + + const byte *songdata; ///< raw data array + size_t songdatalen; ///< length of song data + MidiFile ⌖ ///< recipient of data + + /** Overridden MIDI status codes used in the data format */ + enum MpsMidiStatus { + MPSMIDIST_SEGMENT_RETURN = 0xFD, ///< resume playing master track from stored position + MPSMIDIST_SEGMENT_CALL = 0xFE, ///< store current position of master track playback, and begin playback of a segment + MPSMIDIST_ENDSONG = 0xFF, ///< immediately end the song + }; + + static void AddMidiData(MidiFile::DataBlock &block, byte b1, byte b2) + { + *block.data.Append() = b1; + *block.data.Append() = b2; + } + static void AddMidiData(MidiFile::DataBlock &block, byte b1, byte b2, byte b3) + { + *block.data.Append() = b1; + *block.data.Append() = b2; + *block.data.Append() = b3; + } + + /** + * Construct a TTD DOS music format decoder. + * @param songdata Buffer of song data from CAT file, ownership remains with caller + * @param songdatalen Length of the data buffer in bytes + * @param target MidiFile object to add decoded data to + */ + MpsMachine(const byte *data, size_t length, MidiFile &target) + : songdata(data), songdatalen(length), target(target) + { + uint32 pos = 0; + int loopmax; + int loopidx; + + /* First byte is the initial "tempo" */ + this->initial_tempo = this->songdata[pos++]; + + /* Next byte is a count of callable segments */ + loopmax = this->songdata[pos++]; + for (loopidx = 0; loopidx < loopmax; loopidx++) { + /* Segments form a linked list in the stream, + * first two bytes in each is an offset to the next. + * Two bytes between offset to next and start of data + * are unaccounted for. */ + this->segments.push_back(pos + 4); + pos += FROM_LE16(*(const int16 *)(this->songdata + pos)); + } + + /* After segments follows list of master tracks for each channel, + * also prefixed with a byte counting actual tracks. */ + loopmax = this->songdata[pos++]; + for (loopidx = 0; loopidx < loopmax; loopidx++) { + /* Similar structure to segments list, but also has + * the MIDI channel number as a byte before the offset + * to next track. */ + byte ch = this->songdata[pos++]; + this->channels[ch].startpos = pos + 4; + pos += FROM_LE16(*(const int16 *)(this->songdata + pos)); + } + } + + /** + * Read an SMF-style variable length value (note duration) from songdata. + * @param pos Position to read from, updated to point to next byte after the value read + * @return Value read from data stream + */ + uint16 ReadVariableLength(uint32 &pos) + { + byte b = 0; + uint16 res = 0; + do { + b = this->songdata[pos++]; + res = (res << 7) + (b & 0x7F); + } while (b & 0x80); + return res; + } + + /** + * Prepare for playback from the beginning. Resets the song pointer for every track to the beginning. + */ + void RestartSong() + { + for (int ch = 0; ch < 16; ch++) { + Channel &chandata = this->channels[ch]; + if (chandata.startpos != 0) { + /* Active track, set position to beginning */ + chandata.playpos = chandata.startpos; + chandata.delay = this->ReadVariableLength(chandata.playpos); + } else { + /* Inactive track, mark as such */ + chandata.playpos = 0; + chandata.delay = 0; + } + } + } + + /** + * Play one frame of data from one channel + */ + uint16 PlayChannelFrame(MidiFile::DataBlock &outblock, int channel) + { + uint16 newdelay = 0; + byte b1, b2; + Channel &chandata = this->channels[channel]; + + do { + /* Read command/status byte */ + b1 = this->songdata[chandata.playpos++]; + + /* Command 0xFE, call segment from master track */ + if (b1 == MPSMIDIST_SEGMENT_CALL) { + b1 = this->songdata[chandata.playpos++]; + chandata.returnpos = chandata.playpos; + chandata.playpos = this->segments[b1]; + newdelay = this->ReadVariableLength(chandata.playpos); + if (newdelay == 0) { + continue; + } + return newdelay; + } + + /* Command 0xFD, return from segment to master track */ + if (b1 == MPSMIDIST_SEGMENT_RETURN) { + chandata.playpos = chandata.returnpos; + chandata.returnpos = 0; + newdelay = this->ReadVariableLength(chandata.playpos); + if (newdelay == 0) { + continue; + } + return newdelay; + } + + /* Command 0xFF, end of song */ + if (b1 == MPSMIDIST_ENDSONG) { + this->shouldplayflag = false; + return 0; + } + + /* Regular MIDI channel message status byte */ + if (b1 >= 0x80) { + /* Save the status byte as running status for the channel + * and read another byte for first parameter to command */ + chandata.running_status = b1; + b1 = this->songdata[chandata.playpos++]; + } + + switch (chandata.running_status & 0xF0) { + case MIDIST_NOTEOFF: + case MIDIST_NOTEON: + b2 = this->songdata[chandata.playpos++]; + if (b2 != 0) { + /* Note on, read velocity and scale according to rules */ + int16 velocity; + if (channel == 9) { + /* Percussion channel, fixed velocity scaling not in the table */ + velocity = (int16)b2 * 0x50; + } else { + /* Regular channel, use scaling from table */ + velocity = b2 * programvelocities[chandata.cur_program]; + } + b2 = (velocity / 128) & 0x00FF; + AddMidiData(outblock, MIDIST_NOTEON + channel, b1, b2); + } else { + /* Note off */ + AddMidiData(outblock, MIDIST_NOTEON + channel, b1, 0); + } + break; + case MIDIST_CONTROLLER: + b2 = this->songdata[chandata.playpos++]; + if (b1 == MIDICT_MODE_MONO) { + /* Unknown what the purpose of this is. + * Occurs in "Can't get There from Here" and in "Aliens Ate my Railway" a few times each. + * Possibly intended to give hints to other (non-GM) music drivers decoding the song. + */ + break; + } else if (b1 == 0) { + /* Standard MIDI controller 0 is "bank select", override meaning to change tempo. + * This is not actually used in any of the original songs. */ + if (b2 != 0) { + this->current_tempo = ((int)b2) * 48 / 60; + } + break; + } else if (b1 == MIDICT_EFFECTS1) { + /* Override value of this controller, default mapping is Reverb Send Level according to MMA RP-023. + * Unknown what the purpose of this particular value is. */ + b2 = 30; + } + AddMidiData(outblock, MIDIST_CONTROLLER + channel, b1, b2); + break; + case MIDIST_PROGCHG: + if (b1 == 0x7E) { + /* Program change to "Applause" is originally used + * to cause the song to loop, but that gets handled + * separately in the output driver here. + * Just end the song. */ + this->shouldplayflag = false; + break; + } + /* Used for note velocity scaling lookup */ + chandata.cur_program = b1; + /* Two programs translated to a third, this is likely to + * provide three different velocity scalings of "brass". */ + if (b1 == 0x57 || b1 == 0x3F) { + b1 = 0x3E; + } + AddMidiData(outblock, MIDIST_PROGCHG + channel, b1); + break; + case MIDIST_PITCHBEND: + b2 = this->songdata[chandata.playpos++]; + AddMidiData(outblock, MIDIST_PITCHBEND + channel, b1, b2); + break; + default: + break; + } + + newdelay = this->ReadVariableLength(chandata.playpos); + } while (newdelay == 0); + + return newdelay; + } + + /** + * Play one frame of data into a block. + */ + bool PlayFrame(MidiFile::DataBlock &block) + { + /* Update tempo/ticks counter */ + this->tempo_ticks -= this->current_tempo; + if (this->tempo_ticks > 0) { + return true; + } + this->tempo_ticks += TEMPO_RATE; + + /* Look over all channels, play those active */ + for (int ch = 0; ch < 16; ch++) { + Channel &chandata = this->channels[ch]; + if (chandata.playpos != 0) { + if (chandata.delay == 0) { + chandata.delay = this->PlayChannelFrame(block, ch); + } + chandata.delay--; + } + } + + return this->shouldplayflag; + } + + /** + * Perform playback of whole song. + */ + bool PlayInto() + { + /* Tempo seems to be handled as TEMPO_RATE = 148 ticks per second. + * Use this as the tickdiv, and define the tempo to be one second (1M microseconds) per tickdiv. + * MIDI software loading exported files will show a bogus tempo, but playback will be correct. */ + this->target.tickdiv = TEMPO_RATE; + this->target.tempos.push_back(MidiFile::TempoChange(0, 1000000)); + + /* Initialize playback simulation */ + this->RestartSong(); + this->shouldplayflag = true; + this->current_tempo = (int32)this->initial_tempo * 24 / 60; + this->tempo_ticks = this->current_tempo; + + /* Always reset percussion channel to program 0 */ + this->target.blocks.push_back(MidiFile::DataBlock()); + AddMidiData(this->target.blocks.back(), MIDIST_PROGCHG+9, 0x00); + + /* Technically should be an endless loop, but having + * a maximum (about 10 minutes) avoids getting stuck, + * in case of corrupted data. */ + for (uint32 tick = 0; tick < 100000; tick+=1) { + this->target.blocks.push_back(MidiFile::DataBlock()); + auto &block = this->target.blocks.back(); + block.ticktime = tick; + if (!this->PlayFrame(block)) { + break; + } + } + return true; + } +}; +/** Frames/ticks per second for music playback */ +const int MpsMachine::TEMPO_RATE = 148; +/** Base note velocities for various GM programs */ +const byte MpsMachine::programvelocities[128] = { + 100, 100, 100, 100, 100, 90, 100, 100, 100, 100, 100, 90, 100, 100, 100, 100, + 100, 100, 85, 100, 100, 100, 100, 100, 100, 100, 100, 100, 90, 90, 110, 80, + 100, 100, 100, 90, 70, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 90, 100, 100, 100, 100, 100, 100, 120, 100, 100, 100, 120, 100, 127, + 100, 100, 90, 100, 100, 100, 100, 100, 100, 95, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 115, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, +}; + +/** + * Create MIDI data from song data for the original Microprose music drivers. + * @param data pointer to block of data + * @param length size of data in bytes + * @return true if the data could be loaded + */ +bool MidiFile::LoadMpsData(const byte *data, size_t length) +{ + _midifile_instance = this; + + MpsMachine machine(data, length, *this); + return machine.PlayInto() && FixupMidiData(*this); +} + +bool MidiFile::LoadSong(const MusicSongInfo &song) +{ + switch (song.filetype) { + case MTT_STANDARDMIDI: + return this->LoadFile(song.filename); + case MTT_MPSMIDI: + { + size_t songdatalen = 0; + byte *songdata = GetMusicCatEntryData(song.filename, song.cat_index, songdatalen); + if (songdata != NULL) { + bool result = this->LoadMpsData(songdata, songdatalen); + free(songdata); + return result; + } else { + return false; + } + } + default: + NOT_REACHED(); + } +} + /** * Move data from other to this, and clears other. * @param other object containing loaded data to take over @@ -450,8 +839,280 @@ void MidiFile::MoveFrom(MidiFile &other) std::swap(this->tempos, other.tempos); this->tickdiv = other.tickdiv; + _midifile_instance = this; + other.blocks.clear(); other.tempos.clear(); other.tickdiv = 0; } +static void WriteVariableLen(FILE *f, uint32 value) +{ + if (value < 0x7F) { + byte tb = value; + fwrite(&tb, 1, 1, f); + } else if (value < 0x3FFF) { + byte tb[2]; + tb[1] = value & 0x7F; value >>= 7; + tb[0] = (value & 0x7F) | 0x80; value >>= 7; + fwrite(tb, 1, sizeof(tb), f); + } else if (value < 0x1FFFFF) { + byte tb[3]; + tb[2] = value & 0x7F; value >>= 7; + tb[1] = (value & 0x7F) | 0x80; value >>= 7; + tb[0] = (value & 0x7F) | 0x80; value >>= 7; + fwrite(tb, 1, sizeof(tb), f); + } else if (value < 0x0FFFFFFF) { + byte tb[4]; + tb[3] = value & 0x7F; value >>= 7; + tb[2] = (value & 0x7F) | 0x80; value >>= 7; + tb[1] = (value & 0x7F) | 0x80; value >>= 7; + tb[0] = (value & 0x7F) | 0x80; value >>= 7; + fwrite(tb, 1, sizeof(tb), f); + } +} + +/** + * Write a Standard MIDI File containing the decoded music. + * @param filename Name of file to write to + * @return True if the file was written to completion + */ +bool MidiFile::WriteSMF(const char *filename) +{ + FILE *f = FioFOpenFile(filename, "wb", Subdirectory::NO_DIRECTORY); + if (!f) { + return false; + } + + /* SMF header */ + const byte fileheader[] = { + 'M', 'T', 'h', 'd', // block name + 0x00, 0x00, 0x00, 0x06, // BE32 block length, always 6 bytes + 0x00, 0x00, // writing format 0 (all in one track) + 0x00, 0x01, // containing 1 track (BE16) + (byte)(this->tickdiv >> 8), (byte)this->tickdiv, // tickdiv in BE16 + }; + fwrite(fileheader, sizeof(fileheader), 1, f); + + /* Track header */ + const byte trackheader[] = { + 'M', 'T', 'r', 'k', // block name + 0, 0, 0, 0, // BE32 block length, unknown at this time + }; + fwrite(trackheader, sizeof(trackheader), 1, f); + /* Determine position to write the actual track block length at */ + size_t tracksizepos = ftell(f) - 4; + + /* Write blocks in sequence */ + uint32 lasttime = 0; + size_t nexttempoindex = 0; + for (size_t bi = 0; bi < this->blocks.size(); bi++) { + DataBlock &block = this->blocks[bi]; + TempoChange &nexttempo = this->tempos[nexttempoindex]; + + uint32 timediff = block.ticktime - lasttime; + + /* Check if there is a tempo change before this block */ + if (nexttempo.ticktime < block.ticktime) { + timediff = nexttempo.ticktime - lasttime; + } + + /* Write delta time for block */ + lasttime += timediff; + bool needtime = false; + WriteVariableLen(f, timediff); + + /* Write tempo change if there is one */ + if (nexttempo.ticktime <= block.ticktime) { + byte tempobuf[6] = { MIDIST_SMF_META, 0x51, 0x03, 0, 0, 0 }; + tempobuf[3] = (nexttempo.tempo & 0x00FF0000) >> 16; + tempobuf[4] = (nexttempo.tempo & 0x0000FF00) >> 8; + tempobuf[5] = (nexttempo.tempo & 0x000000FF); + fwrite(tempobuf, sizeof(tempobuf), 1, f); + nexttempoindex++; + needtime = true; + } + /* If a tempo change occurred between two blocks, rather than + * at start of this one, start over with delta time for the block. */ + if (nexttempo.ticktime < block.ticktime) { + /* Start loop over at same index */ + bi--; + continue; + } + + /* Write each block data command */ + byte *dp = block.data.Begin(); + while (dp < block.data.End()) { + /* Always zero delta time inside blocks */ + if (needtime) { + fputc(0, f); + } + needtime = true; + + /* Check message type and write appropriate number of bytes */ + switch (*dp & 0xF0) { + case MIDIST_NOTEOFF: + case MIDIST_NOTEON: + case MIDIST_POLYPRESS: + case MIDIST_CONTROLLER: + case MIDIST_PITCHBEND: + fwrite(dp, 1, 3, f); + dp += 3; + continue; + case MIDIST_PROGCHG: + case MIDIST_CHANPRESS: + fwrite(dp, 1, 2, f); + dp += 2; + continue; + } + + /* Sysex needs to measure length and write that as well */ + if (*dp == MIDIST_SYSEX) { + fwrite(dp, 1, 1, f); + dp++; + byte *sysexend = dp; + while (*sysexend != MIDIST_ENDSYSEX) sysexend++; + ptrdiff_t sysexlen = sysexend - dp; + WriteVariableLen(f, sysexlen); + fwrite(dp, 1, sysexend - dp, f); + dp = sysexend; + continue; + } + + /* Fail for any other commands */ + fclose(f); + return false; + } + } + + /* End of track marker */ + static const byte track_end_marker[] = { 0x00, MIDIST_SMF_META, 0x2F, 0x00 }; + fwrite(&track_end_marker, sizeof(track_end_marker), 1, f); + + /* Fill out the RIFF block length */ + size_t trackendpos = ftell(f); + fseek(f, tracksizepos, SEEK_SET); + uint32 tracksize = (uint32)(trackendpos - tracksizepos - 4); // blindly assume we never produce files larger than 2 GB + tracksize = TO_BE32(tracksize); + fwrite(&tracksize, 4, 1, f); + + fclose(f); + return true; +} + +/** + * Get the name of a Standard MIDI File for a given song. + * For songs already in SMF format, just returns the original. + * Otherwise the song is converted, written to a temporary-ish file, and the written filename is returned. + * @param song Song definition to query + * @return Full filename string, empty string if failed + */ +std::string MidiFile::GetSMFFile(const MusicSongInfo &song) +{ + if (song.filetype == MTT_STANDARDMIDI) { + return std::string(song.filename); + } + + if (song.filetype != MTT_MPSMIDI) return std::string(); + + const char *lastpathsep = strrchr(song.filename, PATHSEPCHAR); + if (lastpathsep == NULL) { + lastpathsep = song.filename; + } + + char basename[MAX_PATH]; + { + /* Remove all '.' characters from filename */ + char *wp = basename; + for (const char *rp = lastpathsep + 1; *rp != '\0'; rp++) { + if (*rp != '.') *wp++ = *rp; + } + *wp++ = '\0'; + } + + char tempdirname[MAX_PATH]; + FioGetFullPath(tempdirname, lastof(tempdirname), Searchpath::SP_AUTODOWNLOAD_DIR, Subdirectory::BASESET_DIR, basename); + if (!AppendPathSeparator(tempdirname, lastof(tempdirname))) return std::string(); + FioCreateDirectory(tempdirname); + + char output_filename[MAX_PATH]; + seprintf(output_filename, lastof(output_filename), "%s%d.mid", tempdirname, song.cat_index); + + if (FileExists(output_filename)) { + /* If the file already exists, assume it's the correct decoded data */ + return std::string(output_filename); + } + + byte *data; + size_t datalen; + data = GetMusicCatEntryData(song.filename, song.cat_index, datalen); + if (data == NULL) return std::string(); + + MidiFile midifile; + if (!midifile.LoadMpsData(data, datalen)) { + free(data); + return std::string(); + } + free(data); + + if (midifile.WriteSMF(output_filename)) { + return std::string(output_filename); + } else { + return std::string(); + } +} + + +static bool CmdDumpSMF(byte argc, char *argv[]) +{ + if (argc == 0) { + IConsolePrint(CC_WARNING, "Write the current song to a Standard MIDI File. Usage: 'dumpsmf '"); + return true; + } + if (argc != 2) { + IConsolePrint(CC_WARNING, "You must specify a filename to write MIDI data to."); + return false; + } + + if (_midifile_instance == NULL) { + IConsolePrint(CC_ERROR, "There is no MIDI file loaded currently, make sure music is playing, and you're using a driver that works with raw MIDI."); + return false; + } + + char fnbuf[MAX_PATH] = { 0 }; + if (seprintf(fnbuf, lastof(fnbuf), "%s%s", FiosGetScreenshotDir(), argv[1]) >= (int)lengthof(fnbuf)) { + IConsolePrint(CC_ERROR, "Filename too long."); + return false; + } + IConsolePrintF(CC_INFO, "Dumping MIDI to: %s", fnbuf); + + if (_midifile_instance->WriteSMF(fnbuf)) { + IConsolePrint(CC_INFO, "File written successfully."); + return true; + } else { + IConsolePrint(CC_ERROR, "An error occurred writing MIDI file."); + return false; + } +} + +static void RegisterConsoleMidiCommands() +{ + static bool registered = false; + if (!registered) { + IConsoleCmdRegister("dumpsmf", CmdDumpSMF); + registered = true; + } +} + +MidiFile::MidiFile() +{ + RegisterConsoleMidiCommands(); +} + +MidiFile::~MidiFile() +{ + if (_midifile_instance == this) { + _midifile_instance = NULL; + } +} + diff --git a/src/music/midifile.hpp b/src/music/midifile.hpp index d077f63cdb..0016be86ca 100644 --- a/src/music/midifile.hpp +++ b/src/music/midifile.hpp @@ -16,6 +16,9 @@ #include "../core/smallvec_type.hpp" #include "midi.h" #include +#include + +struct MusicSongInfo; struct MidiFile { struct DataBlock { @@ -34,9 +37,17 @@ struct MidiFile { std::vector tempos; ///< list of tempo changes in file uint16 tickdiv; ///< ticks per quarter note + MidiFile(); + ~MidiFile(); + bool LoadFile(const char *filename); + bool LoadMpsData(const byte *data, size_t length); + bool LoadSong(const MusicSongInfo &song); void MoveFrom(MidiFile &other); + bool WriteSMF(const char *filename); + + static std::string GetSMFFile(const MusicSongInfo &song); static bool ReadSMFHeader(const char *filename, SMFHeader &header); static bool ReadSMFHeader(FILE *file, SMFHeader &header); }; diff --git a/src/music/music_driver.hpp b/src/music/music_driver.hpp index be09d3ea2b..10a99d2750 100644 --- a/src/music/music_driver.hpp +++ b/src/music/music_driver.hpp @@ -14,6 +14,8 @@ #include "../driver.h" +struct MusicSongInfo; + /** Driver for all music playback. */ class MusicDriver : public Driver { public: @@ -21,7 +23,7 @@ public: * Play a particular song. * @param filename The name of file with the song to play. */ - virtual void PlaySong(const char *filename) = 0; + virtual void PlaySong(const MusicSongInfo &song) = 0; /** * Stop playing the current song. diff --git a/src/music/null_m.h b/src/music/null_m.h index df9f7d80d5..51e1a06656 100644 --- a/src/music/null_m.h +++ b/src/music/null_m.h @@ -21,7 +21,7 @@ public: /* virtual */ void Stop() { } - /* virtual */ void PlaySong(const char *filename) { } + /* virtual */ void PlaySong(const MusicSongInfo &song) { } /* virtual */ void StopSong() { } diff --git a/src/music/os2_m.cpp b/src/music/os2_m.cpp index d7fb97d2d3..1689f00a64 100644 --- a/src/music/os2_m.cpp +++ b/src/music/os2_m.cpp @@ -12,6 +12,8 @@ #include "../stdafx.h" #include "../openttd.h" #include "os2_m.h" +#include "midifile.hpp" +#include "../base_media_base.h" #define INCL_DOS #define INCL_OS2MM @@ -49,11 +51,14 @@ static long CDECL MidiSendCommand(const char *cmd, ...) /** OS/2's music player's factory. */ static FMusicDriver_OS2 iFMusicDriver_OS2; -void MusicDriver_OS2::PlaySong(const char *filename) +void MusicDriver_OS2::PlaySong(const MusicSongInfo &song) { - MidiSendCommand("close all"); + std::string filename = MidiFile::GetSMFFile(song); - if (MidiSendCommand("open %s type sequencer alias song", filename) != 0) { + MidiSendCommand("close all"); + if (filename.empty()) return; + + if (MidiSendCommand("open %s type sequencer alias song", filename.c_str()) != 0) { return; } diff --git a/src/music/os2_m.h b/src/music/os2_m.h index f35e2fdcf6..ac7cd03197 100644 --- a/src/music/os2_m.h +++ b/src/music/os2_m.h @@ -21,7 +21,7 @@ public: /* virtual */ void Stop(); - /* virtual */ void PlaySong(const char *filename); + /* virtual */ void PlaySong(const MusicSongInfo &song); /* virtual */ void StopSong(); diff --git a/src/music/qtmidi.cpp b/src/music/qtmidi.cpp index 9bc6a61740..f8ab150e79 100644 --- a/src/music/qtmidi.cpp +++ b/src/music/qtmidi.cpp @@ -30,7 +30,9 @@ #include "../stdafx.h" #include "qtmidi.h" +#include "midifile.hpp" #include "../debug.h" +#include "../base_media_base.h" #define Rect OTTDRect #define Point OTTDPoint @@ -258,11 +260,14 @@ void MusicDriver_QtMidi::Stop() * * @param filename Path to a MIDI file. */ -void MusicDriver_QtMidi::PlaySong(const char *filename) +void MusicDriver_QtMidi::PlaySong(const MusicSongInfo &song) { if (!_quicktime_started) return; - DEBUG(driver, 2, "qtmidi: trying to play '%s'", filename); + std::string filename = MidiFile::GetSMFFile(song); + if (filename.empty()) return; + + DEBUG(driver, 2, "qtmidi: trying to play '%s'", filename.c_str()); switch (_quicktime_state) { case QT_STATE_PLAY: StopSong(); @@ -276,12 +281,12 @@ void MusicDriver_QtMidi::PlaySong(const char *filename) FALLTHROUGH; case QT_STATE_IDLE: - LoadMovieForMIDIFile(filename, &_quicktime_movie); + LoadMovieForMIDIFile(filename.c_str(), &_quicktime_movie); SetMovieVolume(_quicktime_movie, VOLUME); StartMovie(_quicktime_movie); _quicktime_state = QT_STATE_PLAY; } - DEBUG(driver, 3, "qtmidi: playing '%s'", filename); + DEBUG(driver, 3, "qtmidi: playing '%s'", filename.c_str()); } diff --git a/src/music/qtmidi.h b/src/music/qtmidi.h index f0e17086e4..32163db939 100644 --- a/src/music/qtmidi.h +++ b/src/music/qtmidi.h @@ -20,7 +20,7 @@ public: /* virtual */ void Stop(); - /* virtual */ void PlaySong(const char *filename); + /* virtual */ void PlaySong(const MusicSongInfo &song); /* virtual */ void StopSong(); diff --git a/src/music/win32_m.cpp b/src/music/win32_m.cpp index edaae36fa2..a32318db12 100644 --- a/src/music/win32_m.cpp +++ b/src/music/win32_m.cpp @@ -18,6 +18,7 @@ #include "../debug.h" #include "midifile.hpp" #include "midi.h" +#include "../base_media_base.h" #include "../safeguards.h" @@ -183,7 +184,7 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW /* find first block after start time and pretend playback started earlier * this is to allow all blocks prior to the actual start to still affect playback, * as they may contain important controller and program changes */ - size_t preload_bytes = 0; + uint preload_bytes = 0; for (size_t bl = 0; bl < _midi.current_file.blocks.size(); bl++) { MidiFile::DataBlock &block = _midi.current_file.blocks[bl]; preload_bytes += block.data.Length(); @@ -193,8 +194,13 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW _midi.current_segment.start_block = bl; break; } else { + /* Calculate offset start time for playback. + * The preload_bytes are used to compensate for delay in transmission over traditional serial MIDI interfaces, + * which have a bitrate of 31,250 bits/sec, and transmit 1+8+1 start/data/stop bits per byte. + * The delay compensation is needed to avoid time-compression of following messages. + */ DEBUG(driver, 2, "Win32-MIDI: timer: start from block %d (ticktime %d, realtime %.3f, bytes %d)", (int)bl, (int)block.ticktime, ((int)block.realtime) / 1000.0, (int)preload_bytes); - _midi.playback_start_time -= block.realtime / 1000; + _midi.playback_start_time -= block.realtime / 1000 - preload_bytes * 1000 / 3125; break; } } @@ -208,10 +214,6 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW while (_midi.current_block < _midi.current_file.blocks.size()) { MidiFile::DataBlock &block = _midi.current_file.blocks[_midi.current_block]; - /* check that block is not in the future */ - if (block.realtime / 1000 > playback_time) { - break; - } /* check that block isn't at end-of-song override */ if (_midi.current_segment.end > 0 && block.ticktime >= _midi.current_segment.end) { if (_midi.current_segment.loop) { @@ -222,6 +224,10 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW } break; } + /* check that block is not in the future */ + if (block.realtime / 1000 > playback_time) { + break; + } byte *data = block.data.Begin(); size_t remaining = block.data.Length(); @@ -296,23 +302,27 @@ void CALLBACK TimerCallback(UINT uTimerID, UINT, DWORD_PTR dwUser, DWORD_PTR, DW /* end? */ if (_midi.current_block == _midi.current_file.blocks.size()) { if (_midi.current_segment.loop) { - _midi.current_block = 0; - _midi.playback_start_time = timeGetTime(); + _midi.current_block = _midi.current_segment.start_block; + _midi.playback_start_time = timeGetTime() - _midi.current_file.blocks[_midi.current_block].realtime / 1000; } else { _midi.do_stop = true; } } } -void MusicDriver_Win32::PlaySong(const char *filename) +void MusicDriver_Win32::PlaySong(const MusicSongInfo &song) { DEBUG(driver, 2, "Win32-MIDI: PlaySong: entry"); EnterCriticalSection(&_midi.lock); - _midi.next_file.LoadFile(filename); - _midi.next_segment.start = 0; - _midi.next_segment.end = 0; - _midi.next_segment.loop = false; + if (!_midi.next_file.LoadSong(song)) { + LeaveCriticalSection(&_midi.lock); + return; + } + + _midi.next_segment.start = song.override_start; + _midi.next_segment.end = song.override_end; + _midi.next_segment.loop = song.loop; DEBUG(driver, 2, "Win32-MIDI: PlaySong: setting flag"); _midi.do_stop = _midi.playing; diff --git a/src/music/win32_m.h b/src/music/win32_m.h index 3efee3243a..1ac8ae69e4 100644 --- a/src/music/win32_m.h +++ b/src/music/win32_m.h @@ -21,7 +21,7 @@ public: /* virtual */ void Stop(); - /* virtual */ void PlaySong(const char *filename); + /* virtual */ void PlaySong(const MusicSongInfo &song); /* virtual */ void StopSong(); diff --git a/src/music_gui.cpp b/src/music_gui.cpp index 25bb413146..e7552fe02f 100644 --- a/src/music_gui.cpp +++ b/src/music_gui.cpp @@ -10,6 +10,7 @@ /** @file music_gui.cpp GUI for the music playback. */ #include "stdafx.h" +#include #include "openttd.h" #include "base_media_base.h" #include "music/music_driver.hpp" @@ -35,244 +36,385 @@ #include "safeguards.h" -/** - * Get the name of the song. - * @param index of the song. - * @return the name of the song. - */ -static const char *GetSongName(int index) -{ - 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]; -} +struct MusicSystem { + struct PlaylistEntry : MusicSongInfo { + const MusicSet *set; ///< music set the song comes from + uint set_index; ///< index of song in set -/** The currently played song */ -static byte _music_wnd_cursong = 1; -/** Whether a song is currently played */ -static bool _song_is_active = false; + PlaylistEntry(const MusicSet *set, uint set_index) : MusicSongInfo(set->songinfo[set_index]), set(set), set_index(set_index) { } + bool IsValid() const { return !StrEmpty(this->songname); } + }; + typedef std::vector Playlist; -/** Indices of the songs in the current playlist */ -static byte _cur_playlist[NUM_SONGS_PLAYLIST + 1]; + enum PlaylistChoices { + PLCH_ALLMUSIC, + PLCH_OLDSTYLE, + PLCH_NEWSTYLE, + PLCH_EZYSTREET, + PLCH_CUSTOM1, + PLCH_CUSTOM2, + PLCH_THEMEONLY, + PLCH_MAX, + }; -/** 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]; + Playlist active_playlist; ///< current play order of songs, including any shuffle + Playlist displayed_playlist; ///< current playlist as displayed in GUI, never in shuffled order + Playlist music_set; ///< all songs in current music set, in set order -assert_compile(lengthof(_settings_client.music.custom_1) == NUM_SONGS_PLAYLIST + 1); -assert_compile(lengthof(_settings_client.music.custom_2) == NUM_SONGS_PLAYLIST + 1); + PlaylistChoices selected_playlist; -/** The different playlists that can be played. */ -static byte * const _playlists[] = { - _playlist_all, - _playlist_old_style, - _playlist_new_style, - _playlist_ezy_street, - _settings_client.music.custom_1, - _settings_client.music.custom_2, + void BuildPlaylists(); + + void ChangePlaylist(PlaylistChoices pl); + void ChangeMusicSet(const char *set_name); + void Shuffle(); + void Unshuffle(); + + void Play(); + void Stop(); + void Next(); + void Prev(); + void CheckStatus(); + + bool IsPlaying() const; + bool IsShuffle() const; + PlaylistEntry GetCurrentSong() const; + + bool IsCustomPlaylist() const; + void PlaylistAdd(size_t song_index); + void PlaylistRemove(size_t song_index); + void PlaylistClear(); + +private: + void ChangePlaylistPosition(int ofs); + int playlist_position; + + void SaveCustomPlaylist(PlaylistChoices pl); + + Playlist standard_playlists[PLCH_MAX]; }; -/** - * Validate a playlist. - * @param playlist The playlist to validate. - * @param last The last location in the list. - */ -void ValidatePlaylist(byte *playlist, byte *last) -{ - while (*playlist != 0 && playlist <= last) { - /* Song indices are saved off-by-one so 0 is "nothing". */ - if (*playlist <= NUM_SONGS_AVAILABLE && !StrEmpty(GetSongName(*playlist - 1))) { - playlist++; - continue; - } - for (byte *p = playlist; *p != 0 && p <= last; p++) { - p[0] = p[1]; - } - } +MusicSystem _music; - /* Make sure the list is null terminated. */ - *last = 0; -} -/** Prepare the playlists */ -void InitializeMusic() +/** Rebuild all playlists for the current music set */ +void MusicSystem::BuildPlaylists() { - uint j = 0; + const MusicSet *set = BaseMusic::GetUsedSet(); + + /* Clear current playlists */ + for (size_t i = 0; i < lengthof(this->standard_playlists); ++i) this->standard_playlists[i].clear(); + this->music_set.clear(); + + /* Build standard playlists, and a list of available music */ 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; + PlaylistEntry entry(set, i); + if (!entry.IsValid()) continue; - /* 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; + this->music_set.push_back(entry); + + /* Add theme song to theme-only playlist */ + if (i == 0) this->standard_playlists[PLCH_THEMEONLY].push_back(entry); + + /* Don't add the theme song to standard playlists */ + if (i > 0) { + this->standard_playlists[PLCH_ALLMUSIC].push_back(entry); + uint theme = (i - 1) / NUM_SONGS_CLASS; + this->standard_playlists[PLCH_OLDSTYLE + theme].push_back(entry); } - /* Terminate the list */ - _playlists[k + 1][j] = 0; } - ValidatePlaylist(_settings_client.music.custom_1, lastof(_settings_client.music.custom_1)); - ValidatePlaylist(_settings_client.music.custom_2, lastof(_settings_client.music.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; - byte *p = b; - byte t; - - if (b[0] == 0) return; // empty playlist - - do p++; while (p[0] != 0); // find the end - - t = *--p; // and copy the bytes - while (p != b) { - p--; - p[1] = p[0]; - } - *b = t; - - _song_is_active = false; -} - -static void SkipToNextSong() -{ - byte *b = _cur_playlist; - byte t; - - t = b[0]; - if (t != 0) { - while (b[1] != 0) { - b[0] = b[1]; - b++; + /* Load custom playlists + * Song index offsets are 1-based, zero indicates invalid/end-of-list value */ + for (uint i = 0; i < NUM_SONGS_PLAYLIST; i++) { + if (_settings_client.music.custom_1[i] > 0) { + PlaylistEntry entry(set, _settings_client.music.custom_1[i] - 1); + if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM1].push_back(entry); } - b[0] = t; + if (_settings_client.music.custom_2[i] > 0) { + PlaylistEntry entry(set, _settings_client.music.custom_2[i] - 1); + if (entry.IsValid()) this->standard_playlists[PLCH_CUSTOM2].push_back(entry); + } + } +} + +/** + * Switch to another playlist, or reload the current one. + * @param pl Playlist to select + */ +void MusicSystem::ChangePlaylist(PlaylistChoices pl) +{ + assert(pl < PLCH_MAX && pl >= PLCH_ALLMUSIC); + + this->displayed_playlist = this->standard_playlists[pl]; + this->active_playlist = this->displayed_playlist; + this->selected_playlist = pl; + this->playlist_position = 0; + + if (this->selected_playlist != PLCH_THEMEONLY) _settings_client.music.playlist = this->selected_playlist; + + if (_settings_client.music.shuffle) { + this->Shuffle(); + /* Shuffle() will also Play() if necessary, only start once */ + } else if (_settings_client.music.playing) { + this->Play(); } - _song_is_active = false; + InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); + InvalidateWindowData(WC_MUSIC_WINDOW, 0); } -static void MusicVolumeChanged(byte new_vol) +/** + * Change to named music set, and reset playback. + * @param set_name Name of music set to select + */ +void MusicSystem::ChangeMusicSet(const char *set_name) { - MusicDriver::GetInstance()->SetVolume(new_vol); + BaseMusic::SetSet(set_name); + + this->BuildPlaylists(); + this->ChangePlaylist(this->selected_playlist); + + InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true); } -static void DoPlaySong() +/** Enable shuffle mode and restart playback */ +void MusicSystem::Shuffle() { - char filename[MAX_PATH]; - if (FioFindFullPath(filename, lastof(filename), BASESET_DIR, BaseMusic::GetUsedSet()->files[_music_wnd_cursong - 1].filename) == NULL) { - FioFindFullPath(filename, lastof(filename), OLD_GM_DIR, BaseMusic::GetUsedSet()->files[_music_wnd_cursong - 1].filename); + _settings_client.music.shuffle = true; + + this->active_playlist = this->displayed_playlist; + for (size_t i = 0; i < this->active_playlist.size(); i++) { + size_t shuffle_index = InteractiveRandom() % (this->active_playlist.size() - i); + std::swap(this->active_playlist[i], this->active_playlist[i + shuffle_index]); } - MusicDriver::GetInstance()->PlaySong(filename); - SetWindowDirty(WC_MUSIC_WINDOW, 0); + + if (_settings_client.music.playing) this->Play(); + + InvalidateWindowData(WC_MUSIC_WINDOW, 0); } -static void DoStopMusic() +/** Disable shuffle and restart playback */ +void MusicSystem::Unshuffle() +{ + _settings_client.music.shuffle = false; + this->active_playlist = this->displayed_playlist; + + if (_settings_client.music.playing) this->Play(); + + InvalidateWindowData(WC_MUSIC_WINDOW, 0); +} + +/** Start/restart playback at current song */ +void MusicSystem::Play() +{ + /* Always set the playing flag, even if there is no music */ + _settings_client.music.playing = true; + MusicDriver::GetInstance()->StopSong(); + /* Make sure playlist_position is a valid index, if playlist has changed etc. */ + this->ChangePlaylistPosition(0); + + /* If there is no music, don't try to play it */ + if (this->active_playlist.empty()) return; + + MusicSongInfo song = this->active_playlist[this->playlist_position]; + if (_game_mode == GM_MENU && this->selected_playlist == PLCH_THEMEONLY) song.loop = true; + MusicDriver::GetInstance()->PlaySong(song); + + InvalidateWindowData(WC_MUSIC_WINDOW, 0); +} + +/** Stop playback and set flag that we don't intend to play music */ +void MusicSystem::Stop() { MusicDriver::GetInstance()->StopSong(); - SetWindowDirty(WC_MUSIC_WINDOW, 0); + _settings_client.music.playing = false; + + InvalidateWindowData(WC_MUSIC_WINDOW, 0); } -/** Reload the active playlist data from playlist selection and shuffle setting */ -static void ResetPlaylist() +/** Skip to next track */ +void MusicSystem::Next() { - uint i = 0; - uint j = 0; + this->ChangePlaylistPosition(+1); + if (_settings_client.music.playing) this->Play(); - memset(_cur_playlist, 0, sizeof(_cur_playlist)); - do { - /* File is the index into the file table of the music set. The play list uses 0 as 'no entry', - * so we need to subtract 1. In case of 'no entry' (file = -1), just skip adding it outright. */ - int file = _playlists[_settings_client.music.playlist][i] - 1; - if (file >= 0) { - const char *filename = BaseMusic::GetUsedSet()->files[file].filename; - /* We are now checking for the existence of that file prior - * to add it to the list of available songs */ - if (!StrEmpty(filename) && FioCheckFileExists(filename, BASESET_DIR)) { - _cur_playlist[j] = _playlists[_settings_client.music.playlist][i]; - j++; - } + InvalidateWindowData(WC_MUSIC_WINDOW, 0); +} + +/** Skip to previous track */ +void MusicSystem::Prev() +{ + this->ChangePlaylistPosition(-1); + if (_settings_client.music.playing) this->Play(); + + InvalidateWindowData(WC_MUSIC_WINDOW, 0); +} + +/** Check that music is playing if it should, and that appropriate playlist is active for game/main menu */ +void MusicSystem::CheckStatus() +{ + if ((_game_mode == GM_MENU) != (this->selected_playlist == PLCH_THEMEONLY)) { + /* Make sure the theme-only playlist is active when on the title screen, and not during gameplay */ + this->ChangePlaylist((_game_mode == GM_MENU) ? PLCH_THEMEONLY : (PlaylistChoices)_settings_client.music.playlist); + } + if (this->active_playlist.empty()) return; + /* If we were supposed to be playing, but music has stopped, move to next song */ + if (this->IsPlaying() && !MusicDriver::GetInstance()->IsSongPlaying()) this->Next(); +} + +/** Is the player getting music right now? */ +bool MusicSystem::IsPlaying() const +{ + return _settings_client.music.playing && !this->active_playlist.empty(); +} + +/** Is shuffle mode enabled? */ +bool MusicSystem::IsShuffle() const +{ + return _settings_client.music.shuffle; +} + +/** Return the current song, or a dummy if none */ +MusicSystem::PlaylistEntry MusicSystem::GetCurrentSong() const +{ + if (!this->IsPlaying()) return PlaylistEntry(BaseMusic::GetUsedSet(), 0); + return this->active_playlist[this->playlist_position]; +} + +/** Is one of the custom playlists selected? */ +bool MusicSystem::IsCustomPlaylist() const +{ + return (this->selected_playlist == PLCH_CUSTOM1) || (this->selected_playlist == PLCH_CUSTOM2); +} + +/** + * Append a song to a custom playlist. + * Always adds to the currently active playlist. + * @param song_index Index of song in the current music set to add + */ +void MusicSystem::PlaylistAdd(size_t song_index) +{ + if (!this->IsCustomPlaylist()) return; + + /* Pick out song from the music set */ + if (song_index >= this->music_set.size()) return; + PlaylistEntry entry = this->music_set[song_index]; + + /* Check for maximum length */ + if (this->standard_playlists[this->selected_playlist].size() >= NUM_SONGS_PLAYLIST) return; + + /* Add it to the appropriate playlist, and the display */ + this->standard_playlists[this->selected_playlist].push_back(entry); + this->displayed_playlist.push_back(entry); + + /* Add it to the active playlist, if playback is shuffled select a random position to add at */ + if (this->active_playlist.empty()) { + this->active_playlist.push_back(entry); + if (this->IsPlaying()) this->Play(); + } else if (this->IsShuffle()) { + /* Generate a random position between 0 and n (inclusive, new length) to insert at */ + size_t maxpos = this->displayed_playlist.size(); + size_t newpos = InteractiveRandom() % maxpos; + this->active_playlist.insert(this->active_playlist.begin() + newpos, entry); + /* Make sure to shift up the current playback position if the song was inserted before it */ + if ((int)newpos <= this->playlist_position) this->playlist_position++; + } else { + this->active_playlist.push_back(entry); + } + + this->SaveCustomPlaylist(this->selected_playlist); + + InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); +} + +/** + * Remove a song from a custom playlist. + * @param song_index Index in the custom playlist to remove. + */ +void MusicSystem::PlaylistRemove(size_t song_index) +{ + if (!this->IsCustomPlaylist()) return; + + Playlist &pl = this->standard_playlists[this->selected_playlist]; + if (song_index >= pl.size()) return; + + /* Remove from "simple" playlists */ + PlaylistEntry song = pl[song_index]; + pl.erase(pl.begin() + song_index); + this->displayed_playlist.erase(this->displayed_playlist.begin() + song_index); + + /* Find in actual active playlist (may be shuffled) and remove, + * if it's the current song restart playback */ + for (size_t i = 0; i < this->active_playlist.size(); i++) { + Playlist::iterator s2 = this->active_playlist.begin() + i; + if (s2->filename == song.filename && s2->cat_index == song.cat_index) { + this->active_playlist.erase(s2); + if ((int)i == this->playlist_position && this->IsPlaying()) this->Play(); + break; } - } while (_playlists[_settings_client.music.playlist][++i] != 0 && j < lengthof(_cur_playlist) - 1); + } - /* Do not shuffle when on the intro-start window, as the song to play has to be the original TTD Theme*/ - if (_settings_client.music.shuffle && _game_mode != GM_MENU) { - i = 500; - do { - uint32 r = InteractiveRandom(); - byte *a = &_cur_playlist[GB(r, 0, 5)]; - byte *b = &_cur_playlist[GB(r, 8, 5)]; + this->SaveCustomPlaylist(this->selected_playlist); - if (*a != 0 && *b != 0) { - byte t = *a; - *a = *b; - *b = t; - } - } while (--i); + InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); +} + +/** + * Remove all songs from the current custom playlist. + * Effectively stops playback too. + */ +void MusicSystem::PlaylistClear() +{ + if (!this->IsCustomPlaylist()) return; + + this->standard_playlists[this->selected_playlist].clear(); + this->ChangePlaylist(this->selected_playlist); + + this->SaveCustomPlaylist(this->selected_playlist); +} + +/** + * Change playlist position pointer by the given offset, making sure to keep it within valid range. + * If the playlist is empty, position is always set to 0. + * @param ofs Amount to move playlist position by. + */ +void MusicSystem::ChangePlaylistPosition(int ofs) +{ + if (this->active_playlist.empty()) { + this->playlist_position = 0; + } else { + this->playlist_position += ofs; + while (this->playlist_position >= (int)this->active_playlist.size()) this->playlist_position -= (int)this->active_playlist.size(); + while (this->playlist_position < 0) this->playlist_position += (int)this->active_playlist.size(); } } -static void StopMusic() +/** + * Save a custom playlist to settings after modification. + * @param pl Playlist to store back + */ +void MusicSystem::SaveCustomPlaylist(PlaylistChoices pl) { - _music_wnd_cursong = 0; - DoStopMusic(); - _song_is_active = false; - SetWindowWidgetDirty(WC_MUSIC_WINDOW, 0, 9); -} - -/** Begin playing the next song on the playlist */ -static void PlayPlaylistSong() -{ - if (_cur_playlist[0] == 0) { - ResetPlaylist(); - /* if there is not songs in the playlist, it may indicate - * no file on the gm folder, or even no gm folder. - * Stop the playback, then */ - if (_cur_playlist[0] == 0) { - _song_is_active = false; - _music_wnd_cursong = 0; - _settings_client.music.playing = false; - return; - } + byte *settings_pl; + if (pl == PLCH_CUSTOM1) { + settings_pl = _settings_client.music.custom_1; + } else if (pl == PLCH_CUSTOM2) { + settings_pl = _settings_client.music.custom_2; + } else { + return; } - _music_wnd_cursong = _cur_playlist[0]; - DoPlaySong(); - _song_is_active = true; - SetWindowWidgetDirty(WC_MUSIC_WINDOW, 0, 9); + size_t num = 0; + MemSetT(settings_pl, 0, NUM_SONGS_PLAYLIST); + + for (Playlist::const_iterator song = this->standard_playlists[pl].begin(); song != this->standard_playlists[pl].end(); ++song) { + /* Music set indices in the settings playlist are 1-based, 0 means unused slot */ + settings_pl[num++] = (byte)song->set_index + 1; + } } -void ResetMusic() -{ - _music_wnd_cursong = 1; - DoPlaySong(); -} /** * Check music playback status and start/stop/song-finished. @@ -280,30 +422,7 @@ void ResetMusic() */ void MusicLoop() { - if (!_settings_client.music.playing && _song_is_active) { - StopMusic(); - } else if (_settings_client.music.playing && !_song_is_active) { - PlayPlaylistSong(); - } - - if (!_song_is_active) return; - - if (!MusicDriver::GetInstance()->IsSongPlaying()) { - if (_game_mode != GM_MENU) { - StopMusic(); - SkipToNextSong(); - PlayPlaylistSong(); - } else { - ResetMusic(); - } - } -} - -static void SelectPlaylist(byte list) -{ - _settings_client.music.playlist = list; - InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); - InvalidateWindowData(WC_MUSIC_WINDOW, 0); + _music.CheckStatus(); } /** @@ -313,29 +432,20 @@ static void SelectPlaylist(byte list) void ChangeMusicSet(int index) { if (BaseMusic::GetIndexOfUsedSet() == index) return; - - /* Resume playback after switching? - * Always if music is already playing, and also if the user is switching - * away from an empty music set. - * If the user switches away from an empty set, assume it's because they - * want to hear music now. */ - bool shouldplay = _song_is_active || (BaseMusic::GetUsedSet()->num_available == 0); - StopMusic(); - const char *name = BaseMusic::GetSet(index)->name; - BaseMusic::SetSet(name); - free(BaseMusic::ini_set); - BaseMusic::ini_set = stredup(name); - - InitializeMusic(); - ResetPlaylist(); - _settings_client.music.playing = shouldplay; - - InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0); - InvalidateWindowData(WC_MUSIC_WINDOW, 0); - InvalidateWindowData(WC_GAME_OPTIONS, WN_GAME_OPTIONS_GAME_OPTIONS, 0, true); + _music.ChangeMusicSet(name); } +/** + * Prepare the music system for use. + * Called from \c InitializeGame + */ +void InitializeMusic() +{ + _music.BuildPlaylists(); +} + + struct MusicTrackSelectionWindow : public Window { MusicTrackSelectionWindow(WindowDesc *desc, WindowNumber number) : Window(desc) { @@ -392,13 +502,10 @@ struct MusicTrackSelectionWindow : public Window { case WID_MTS_LIST_LEFT: case WID_MTS_LIST_RIGHT: { Dimension d = {0, 0}; - for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) { - const char *song_name = GetSongName(i); - if (StrEmpty(song_name)) continue; - - SetDParam(0, GetTrackNumber(i)); + for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) { + SetDParam(0, song->tracknr); SetDParam(1, 2); - SetDParamStr(2, GetSongName(i)); + SetDParamStr(2, song->songname); Dimension d2 = GetStringBoundingBox(STR_PLAYLIST_TRACK_NAME); d.width = max(d.width, d2.width); d.height += d2.height; @@ -418,13 +525,10 @@ struct MusicTrackSelectionWindow : public Window { GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK); int y = r.top + WD_FRAMERECT_TOP; - for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) { - const char *song_name = GetSongName(i); - if (StrEmpty(song_name)) continue; - - SetDParam(0, GetTrackNumber(i)); + for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) { + SetDParam(0, song->tracknr); SetDParam(1, 2); - SetDParamStr(2, song_name); + SetDParamStr(2, song->songname); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME); y += FONT_HEIGHT_SMALL; } @@ -435,11 +539,10 @@ struct MusicTrackSelectionWindow : public Window { GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK); int y = r.top + WD_FRAMERECT_TOP; - for (const byte *p = _playlists[_settings_client.music.playlist]; *p != 0; p++) { - uint i = *p - 1; - SetDParam(0, GetTrackNumber(i)); + for (MusicSystem::Playlist::const_iterator song = _music.active_playlist.begin(); song != _music.active_playlist.end(); ++song) { + SetDParam(0, song->tracknr); SetDParam(1, 2); - SetDParamStr(2, GetSongName(i)); + SetDParamStr(2, song->songname); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME); y += FONT_HEIGHT_SMALL; } @@ -453,42 +556,13 @@ struct MusicTrackSelectionWindow : public Window { switch (widget) { case WID_MTS_LIST_LEFT: { // add to playlist int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL); - - if (_settings_client.music.playlist < 4) return; - if (!IsInsideMM(y, 0, BaseMusic::GetUsedSet()->num_available)) return; - - byte *p = _playlists[_settings_client.music.playlist]; - for (uint i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) { - if (p[i] == 0) { - /* 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(); - ResetPlaylist(); - break; - } - } + _music.PlaylistAdd(y); break; } case WID_MTS_LIST_RIGHT: { // remove from playlist int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL); - - if (_settings_client.music.playlist < 4) return; - if (!IsInsideMM(y, 0, NUM_SONGS_PLAYLIST)) return; - - byte *p = _playlists[_settings_client.music.playlist]; - for (uint i = y; i != NUM_SONGS_PLAYLIST - 1; i++) { - p[i] = p[i + 1]; - } - - this->SetDirty(); - ResetPlaylist(); + _music.PlaylistRemove(y); break; } @@ -500,17 +574,12 @@ struct MusicTrackSelectionWindow : public Window { } case WID_MTS_CLEAR: // clear - for (uint i = 0; _playlists[_settings_client.music.playlist][i] != 0; i++) _playlists[_settings_client.music.playlist][i] = 0; - this->SetDirty(); - StopMusic(); - ResetPlaylist(); + _music.PlaylistClear(); break; case WID_MTS_ALL: case WID_MTS_OLD: case WID_MTS_NEW: case WID_MTS_EZY: case WID_MTS_CUSTOM1: case WID_MTS_CUSTOM2: // set playlist - SelectPlaylist(widget - WID_MTS_ALL); - StopMusic(); - ResetPlaylist(); + _music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_MTS_ALL)); break; } } @@ -625,8 +694,8 @@ struct MusicWindow : public Window { case WID_M_TRACK_NAME: { Dimension d = GetStringBoundingBox(STR_MUSIC_TITLE_NONE); - for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) { - SetDParamStr(0, GetSongName(i)); + for (MusicSystem::Playlist::const_iterator song = _music.music_set.begin(); song != _music.music_set.end(); ++song) { + SetDParamStr(0, song->songname); d = maxdim(d, GetStringBoundingBox(STR_MUSIC_TITLE_NAME)); } d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT; @@ -652,8 +721,8 @@ struct MusicWindow : public Window { break; } StringID str = STR_MUSIC_TRACK_NONE; - if (_song_is_active != 0 && _music_wnd_cursong != 0) { - SetDParam(0, GetTrackNumber(_music_wnd_cursong - 1)); + if (_music.IsPlaying()) { + SetDParam(0, _music.GetCurrentSong().tracknr); SetDParam(1, 2); str = STR_MUSIC_TRACK_DIGIT; } @@ -666,9 +735,9 @@ struct MusicWindow : public Window { StringID str = STR_MUSIC_TITLE_NONE; if (BaseMusic::GetUsedSet()->num_available == 0) { str = STR_MUSIC_TITLE_NOMUSIC; - } else if (_song_is_active != 0 && _music_wnd_cursong != 0) { + } else if (_music.IsPlaying()) { str = STR_MUSIC_TITLE_NAME; - SetDParamStr(0, GetSongName(_music_wnd_cursong - 1)); + SetDParamStr(0, _music.GetCurrentSong().songname); } DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_FROMSTRING, SA_HOR_CENTER); break; @@ -708,23 +777,19 @@ struct MusicWindow : public Window { { switch (widget) { case WID_M_PREV: // skip to prev - if (!_song_is_active) return; - SkipToPrevSong(); - this->SetDirty(); + _music.Prev(); break; case WID_M_NEXT: // skip to next - if (!_song_is_active) return; - SkipToNextSong(); - this->SetDirty(); + _music.Next(); break; case WID_M_STOP: // stop playing - _settings_client.music.playing = false; + _music.Stop(); break; case WID_M_PLAY: // start playing - _settings_client.music.playing = true; + _music.Play(); break; case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: { // volume sliders @@ -739,7 +804,7 @@ struct MusicWindow : public Window { if (new_vol < 3) new_vol = 0; if (new_vol != *vol) { *vol = new_vol; - if (widget == WID_M_MUSIC_VOL) MusicVolumeChanged(new_vol); + if (widget == WID_M_MUSIC_VOL) MusicDriver::GetInstance()->SetVolume(new_vol); this->SetDirty(); } @@ -748,12 +813,13 @@ struct MusicWindow : public Window { } case WID_M_SHUFFLE: // toggle shuffle - _settings_client.music.shuffle ^= 1; - this->SetWidgetLoweredState(WID_M_SHUFFLE, _settings_client.music.shuffle); + if (_music.IsShuffle()) { + _music.Unshuffle(); + } else { + _music.Shuffle(); + } + this->SetWidgetLoweredState(WID_M_SHUFFLE, _music.IsShuffle()); this->SetWidgetDirty(WID_M_SHUFFLE); - StopMusic(); - ResetPlaylist(); - this->SetDirty(); break; case WID_M_PROGRAMME: // show track selection @@ -762,10 +828,7 @@ struct MusicWindow : public Window { case WID_M_ALL: case WID_M_OLD: case WID_M_NEW: case WID_M_EZY: case WID_M_CUSTOM1: case WID_M_CUSTOM2: // playlist - SelectPlaylist(widget - WID_M_ALL); - StopMusic(); - ResetPlaylist(); - this->SetDirty(); + _music.ChangePlaylist((MusicSystem::PlaylistChoices)(widget - WID_M_ALL)); break; } } diff --git a/src/network/core/tcp_game.cpp b/src/network/core/tcp_game.cpp index 9b3f7b5ef3..caa378fc4c 100644 --- a/src/network/core/tcp_game.cpp +++ b/src/network/core/tcp_game.cpp @@ -45,6 +45,8 @@ NetworkRecvStatus NetworkGameSocketHandler::CloseConnection(bool error) { /* Clients drop back to the main menu */ if (!_network_server && _networking) { + extern void ClientNetworkEmergencySave(); // from network_client.cpp + ClientNetworkEmergencySave(); _switch_mode = SM_MENU; _networking = false; ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL); diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index e3bcbb2d87..4b066681d7 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -119,6 +119,19 @@ struct PacketReader : LoadFilter { }; +/** + * Create an emergency savegame when the network connection is lost. + */ +void ClientNetworkEmergencySave() +{ + if (!_settings_client.gui.autosave_on_network_disconnect) return; + + const char *filename = "netsave.sav"; + DEBUG(net, 0, "Client: Performing emergency save (%s)", filename); + SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false); +} + + /** * Create a new socket for the client side of the game connection. * @param s The socket to connect with. @@ -670,6 +683,9 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet *p ShowErrorMessage(err, INVALID_STRING_ID, WL_CRITICAL); + /* Perform an emergency save if we had already entered the game */ + if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave(); + DeleteWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN); return NETWORK_RECV_STATUS_SERVER_ERROR; @@ -1051,6 +1067,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_SHUTDOWN(Packet ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_SHUTDOWN, INVALID_STRING_ID, WL_CRITICAL); } + if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave(); + return NETWORK_RECV_STATUS_SERVER_ERROR; } @@ -1066,6 +1084,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEWGAME(Packet ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_REBOOT, INVALID_STRING_ID, WL_CRITICAL); } + if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave(); + return NETWORK_RECV_STATUS_SERVER_ERROR; } @@ -1153,6 +1173,7 @@ void ClientNetworkGameSocketHandler::CheckConnection() if (lag > 20) { this->NetworkGameSocketHandler::CloseConnection(); ShowErrorMessage(STR_NETWORK_ERROR_LOSTCONNECTION, INVALID_STRING_ID, WL_CRITICAL); + ClientNetworkEmergencySave(); return; } diff --git a/src/newgrf.cpp b/src/newgrf.cpp index d249b5e155..4cd61b95cf 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -4741,7 +4741,7 @@ static void NewSpriteGroup(ByteReader *buf) } } - group->num_ranges = optimised.size(); + group->num_ranges = (uint)optimised.size(); // cast is safe, there should never be 2**31 elements here if (group->num_ranges > 0) { group->ranges = MallocT(group->num_ranges); MemCpyT(group->ranges, &optimised.front(), group->num_ranges); diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp index 78bbc52443..40a966a166 100644 --- a/src/newgrf_object.cpp +++ b/src/newgrf_object.cpp @@ -96,7 +96,9 @@ uint ObjectSpec::Index() const void ResetObjects() { /* Clean the pool. */ - MemSetT(_object_specs, 0, lengthof(_object_specs)); + for (uint16 i = 0; i < NUM_OBJECTS; i++) { + _object_specs[i] = {}; + } /* And add our originals. */ MemCpyT(_object_specs, _original_objects, lengthof(_original_objects)); diff --git a/src/openttd.cpp b/src/openttd.cpp index 03b9126f55..cce94e35af 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -373,8 +373,7 @@ static void LoadIntroGame(bool load_newgrfs = true) CheckForMissingGlyphs(); - /* Play main theme */ - if (MusicDriver::GetInstance()->IsSongPlaying()) ResetMusic(); + MusicLoop(); // ensure music is correct } void MakeNewgameSettingsLive() diff --git a/src/os/windows/string_uniscribe.cpp b/src/os/windows/string_uniscribe.cpp new file mode 100644 index 0000000000..ea8b8c0224 --- /dev/null +++ b/src/os/windows/string_uniscribe.cpp @@ -0,0 +1,610 @@ +/* $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 . + */ + +/** @file string_uniscribe.cpp Functions related to laying out text on Win32. */ + +#if defined(WITH_UNISCRIBE) + +#include "../../stdafx.h" +#include "../../debug.h" +#include "string_uniscribe.h" +#include "../../language.h" +#include "../../strings_func.h" +#include "../../string_func.h" +#include "../../table/control_codes.h" +#include "win32.h" +#include + +#include +#include + +#include "../../safeguards.h" + +#ifdef _MSC_VER +# pragma comment(lib, "usp10") +#endif + + +/** Uniscribe cache for internal font information, cleared when OTTD changes fonts. */ +static SCRIPT_CACHE _script_cache[FS_END]; + +/** + * Contains all information about a run of characters. A run are consecutive + * characters that share a single font and language. + */ +struct UniscribeRun { + int pos; + int len; + Font *font; + + std::vector ft_glyphs; + + SCRIPT_ANALYSIS sa; + std::vector char_to_glyph; + + std::vector vis_attribs; + std::vector glyphs; + std::vector advances; + std::vector offsets; + int total_advance; + + UniscribeRun(int pos, int len, Font *font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {} +}; + +/** Break a string into language formatting ranges. */ +static std::vector UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32 length); +/** Generate and place glyphs for a run of characters. */ +static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range); + +/** + * Wrapper for doing layouts with Uniscribe. + */ +class UniscribeParagraphLayout : public ParagraphLayouter { +private: + const UniscribeParagraphLayoutFactory::CharType *text_buffer; + + std::vector ranges; ///< All runs of the text. + std::vector::iterator cur_range; ///< The next run to be output. + int cur_range_offset = 0; ///< Offset from the start of the current run from where to output. + +public: + /** Visual run contains data about the bit of text with the same font. */ + class UniscribeVisualRun : public ParagraphLayouter::VisualRun { + private: + std::vector glyphs; + std::vector positions; + std::vector char_to_glyph; + + int start_pos; + int total_advance; + int num_glyphs; + Font *font; + + mutable int *glyph_to_char = NULL; + + public: + UniscribeVisualRun(const UniscribeRun &range, int x); + virtual ~UniscribeVisualRun() + { + free(this->glyph_to_char); + } + + virtual const GlyphID *GetGlyphs() const { return &this->glyphs[0]; } + virtual const float *GetPositions() const { return &this->positions[0]; } + virtual const int *GetGlyphToCharMap() const; + + virtual const Font *GetFont() const { return this->font; } + virtual int GetLeading() const { return this->font->fc->GetHeight(); } + virtual int GetGlyphCount() const { return this->num_glyphs; } + int GetAdvance() const { return this->total_advance; } + }; + + /** A single line worth of VisualRuns. */ + class UniscribeLine : public AutoDeleteSmallVector, public ParagraphLayouter::Line { + public: + virtual int GetLeading() const; + virtual int GetWidth() const; + virtual int CountRuns() const { return this->Length(); } + virtual const VisualRun *GetVisualRun(int run) const { return *this->Get(run); } + + int GetInternalCharLength(WChar c) const + { + /* Uniscribe uses UTF-16 internally which means we need to account for surrogate pairs. */ + return c >= 0x010000U ? 2 : 1; + } + }; + + UniscribeParagraphLayout(std::vector &ranges, const UniscribeParagraphLayoutFactory::CharType *buffer) : text_buffer(buffer), ranges(ranges) + { + this->Reflow(); + } + + virtual ~UniscribeParagraphLayout() {} + + virtual void Reflow() + { + this->cur_range = this->ranges.begin(); + this->cur_range_offset = 0; + } + + virtual const Line *NextLine(int max_width); +}; + +void UniscribeResetScriptCache(FontSize size) +{ + if (_script_cache[size] != NULL) { + ScriptFreeCache(&_script_cache[size]); + _script_cache[size] = NULL; + } +} + +/** Load the matching native Windows font. */ +static HFONT HFontFromFont(Font *font) +{ + LOGFONT logfont; + ZeroMemory(&logfont, sizeof(LOGFONT)); + logfont.lfHeight = font->fc->GetHeight(); + logfont.lfWeight = FW_NORMAL; + logfont.lfCharSet = DEFAULT_CHARSET; + convert_to_fs(font->fc->GetFontName(), logfont.lfFaceName, lengthof(logfont.lfFaceName)); + + return CreateFontIndirect(&logfont); +} + +/** Determine the glyph positions for a run. */ +static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range) +{ + /* Initial size guess for the number of glyphs recommended by Uniscribe. */ + range.glyphs.resize(range.len * 3 / 2 + 16); + range.vis_attribs.resize(range.glyphs.size()); + + /* The char-to-glyph array is the same size as the input. */ + range.char_to_glyph.resize(range.len); + + HDC temp_dc = NULL; + HFONT old_font = NULL; + HFONT cur_font = NULL; + + while (true) { + /* Shape the text run by determining the glyphs needed for display. */ + int glyphs_used = 0; + HRESULT hr = ScriptShape(temp_dc, &_script_cache[range.font->fc->GetSize()], buff + range.pos, range.len, (int)range.glyphs.size(), &range.sa, &range.glyphs[0], &range.char_to_glyph[0], &range.vis_attribs[0], &glyphs_used); + + if (SUCCEEDED(hr)) { + range.glyphs.resize(glyphs_used); + range.vis_attribs.resize(glyphs_used); + + /* Calculate the glyph positions. */ + ABC abc; + range.advances.resize(range.glyphs.size()); + range.offsets.resize(range.glyphs.size()); + hr = ScriptPlace(temp_dc, &_script_cache[range.font->fc->GetSize()], &range.glyphs[0], (int)range.glyphs.size(), &range.vis_attribs[0], &range.sa, &range.advances[0], &range.offsets[0], &abc); + if (SUCCEEDED(hr)) { + /* We map our special sprite chars to values that don't fit into a WORD. Copy the glyphs + * into a new vector and query the real glyph to use for these special chars. */ + range.ft_glyphs.resize(range.glyphs.size()); + for (size_t g_id = 0; g_id < range.glyphs.size(); g_id++) { + range.ft_glyphs[g_id] = range.glyphs[g_id]; + } + for (int i = 0; i < range.len; i++) { + if (buff[range.pos + i] >= SCC_SPRITE_START && buff[range.pos + i] <= SCC_SPRITE_END) { + range.ft_glyphs[range.char_to_glyph[i]] = range.font->fc->MapCharToGlyph(buff[range.pos + i]); + } + } + + /* FreeType and GDI/Uniscribe seems to occasionally disagree over the width of a glyph. */ + range.total_advance = 0; + for (size_t i = 0; i < range.advances.size(); i++) { + if (range.advances[i] > 0 && range.ft_glyphs[i] != 0xFFFF) range.advances[i] = range.font->fc->GetGlyphWidth(range.ft_glyphs[i]); + range.total_advance += range.advances[i]; + } + break; + } + } + + if (hr == E_OUTOFMEMORY) { + /* The glyph buffer needs to be larger. Just double it every time. */ + range.glyphs.resize(range.glyphs.size() * 2); + range.vis_attribs.resize(range.vis_attribs.size() * 2); + } else if (hr == E_PENDING) { + /* Glyph data is not in cache, load native font. */ + cur_font = HFontFromFont(range.font); + if (cur_font == NULL) return false; // Sorry, no dice. + + temp_dc = CreateCompatibleDC(NULL); + SetMapMode(temp_dc, MM_TEXT); + old_font = (HFONT)SelectObject(temp_dc, cur_font); + } else if (hr == USP_E_SCRIPT_NOT_IN_FONT && range.sa.eScript != SCRIPT_UNDEFINED) { + /* Try again with the generic shaping engine. */ + range.sa.eScript = SCRIPT_UNDEFINED; + } else { + /* Some unknown other error. */ + if (temp_dc != NULL) { + SelectObject(temp_dc, old_font); + DeleteObject(cur_font); + ReleaseDC(NULL, temp_dc); + } + return false; + } + } + + if (temp_dc != NULL) { + SelectObject(temp_dc, old_font); + DeleteObject(cur_font); + ReleaseDC(NULL, temp_dc); + } + + return true; +} + +static std::vector UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32 length) +{ + /* Itemize text. */ + SCRIPT_CONTROL control; + ZeroMemory(&control, sizeof(SCRIPT_CONTROL)); + control.uDefaultLanguage = _current_language->winlangid; + + SCRIPT_STATE state; + ZeroMemory(&state, sizeof(SCRIPT_STATE)); + state.uBidiLevel = _current_text_dir == TD_RTL ? 1 : 0; + + std::vector items(16); + while (true) { + /* We subtract one from max_items to work around a buffer overflow on some older versions of Windows. */ + int generated = 0; + HRESULT hr = ScriptItemize(buff, length, (int)items.size() - 1, &control, &state, &items[0], &generated); + + if (SUCCEEDED(hr)) { + /* Resize the item buffer. Note that Uniscribe will always add an additional end sentinel item. */ + items.resize(generated + 1); + break; + } + /* Some kind of error except item buffer too small. */ + if (hr != E_OUTOFMEMORY) return std::vector(); + + items.resize(items.size() * 2); + } + + return items; +} + +/* static */ ParagraphLayouter *UniscribeParagraphLayoutFactory::GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &fontMapping) +{ + int32 length = buff_end - buff; + /* Can't layout an empty string. */ + if (length == 0) return NULL; + + /* Can't layout our in-built sprite fonts. */ + for (FontMap::const_iterator i = fontMapping.Begin(); i != fontMapping.End(); i++) { + if (i->second->fc->IsBuiltInFont()) return NULL; + } + + /* Itemize text. */ + std::vector items = UniscribeItemizeString(buff, length); + if (items.size() == 0) return NULL; + + /* Build ranges from the items and the font map. A range is a run of text + * that is part of a single item and formatted using a single font style. */ + std::vector ranges; + + int cur_pos = 0; + std::vector::iterator cur_item = items.begin(); + for (FontMap::const_iterator i = fontMapping.Begin(); i != fontMapping.End(); i++) { + while (cur_pos < i->first && cur_item != items.end() - 1) { + /* Add a range that spans the intersection of the remaining item and font run. */ + int stop_pos = min(i->first, (cur_item + 1)->iCharPos); + assert(stop_pos - cur_pos > 0); + ranges.push_back(UniscribeRun(cur_pos, stop_pos - cur_pos, i->second, cur_item->a)); + + /* Shape the range. */ + if (!UniscribeShapeRun(buff, ranges.back())) { + return NULL; + } + + /* If we are at the end of the current item, advance to the next item. */ + if (stop_pos == (cur_item + 1)->iCharPos) cur_item++; + cur_pos = stop_pos; + } + } + + return new UniscribeParagraphLayout(ranges, buff); +} + +/* virtual */ const ParagraphLayouter::Line *UniscribeParagraphLayout::NextLine(int max_width) +{ + std::vector::iterator start_run = this->cur_range; + std::vector::iterator last_run = this->cur_range; + + if (start_run == this->ranges.end()) return NULL; + + /* Add remaining width of the first run if it is a broken run. */ + int cur_width = 0; + if (this->cur_range_offset != 0) { + std::vector dx(start_run->len); + ScriptGetLogicalWidths(&start_run->sa, start_run->len, (int)start_run->glyphs.size(), &start_run->advances[0], &start_run->char_to_glyph[0], &start_run->vis_attribs[0], &dx[0]); + + for (std::vector::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) { + cur_width += *c; + } + ++last_run; + } + + /* Gather runs until the line is full. */ + while (last_run != this->ranges.end() && cur_width < max_width) { + cur_width += last_run->total_advance; + ++last_run; + } + + /* If the text does not fit into the available width, find a suitable breaking point. */ + int remaing_offset = (last_run - 1)->len; + if (cur_width > max_width) { + std::vector log_attribs; + + /* Get word break information. */ + int width_avail = max_width; + int num_chars = this->cur_range_offset; + int start_offs = this->cur_range_offset; + int last_cluster = this->cur_range_offset + 1; + for (std::vector::iterator r = start_run; r != last_run; r++) { + log_attribs.resize(r->pos - start_run->pos + r->len); + if (FAILED(ScriptBreak(this->text_buffer + r->pos + start_offs, r->len - start_offs, &r->sa, &log_attribs[r->pos - start_run->pos + start_offs]))) return NULL; + + std::vector dx(r->len); + ScriptGetLogicalWidths(&r->sa, r->len, (int)r->glyphs.size(), &r->advances[0], &r->char_to_glyph[0], &r->vis_attribs[0], &dx[0]); + + /* Count absolute max character count on the line. */ + for (int c = start_offs; c < r->len && width_avail > 0; c++, num_chars++) { + if (c > start_offs && log_attribs[num_chars].fCharStop) last_cluster = num_chars; + width_avail -= dx[c]; + } + + start_offs = 0; + } + + /* Walk backwards to find the last suitable breaking point. */ + while (--num_chars > this->cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {} + + if (num_chars == this->cur_range_offset) { + /* Didn't find any suitable word break point, just break on the last cluster boundary. */ + num_chars = last_cluster; + } + + /* Include whitespace characters after the breaking point. */ + while (num_chars < (int)log_attribs.size() && log_attribs[num_chars].fWhiteSpace) { + num_chars++; + } + + /* Get last run that corresponds to the number of characters to show. */ + for (std::vector::iterator run = start_run; run != last_run; run++) { + num_chars -= run->len; + + if (num_chars <= 0) { + remaing_offset = num_chars + run->len + 1; + last_run = run + 1; + assert(remaing_offset - 1 > 0); + break; + } + } + } + + /* Build display order from the runs. */ + std::vector bidi_level; + for (std::vector::iterator r = start_run; r != last_run; r++) { + bidi_level.push_back(r->sa.s.uBidiLevel); + } + std::vector vis_to_log(bidi_level.size()); + if (FAILED(ScriptLayout((int)bidi_level.size(), &bidi_level[0], &vis_to_log[0], NULL))) return NULL; + + /* Create line. */ + UniscribeLine *line = new UniscribeLine(); + + int cur_pos = 0; + for (std::vector::iterator l = vis_to_log.begin(); l != vis_to_log.end(); l++) { + std::vector::iterator i_run = start_run + *l; + UniscribeRun run = *i_run; + + /* Partial run after line break (either start or end)? Reshape run to get the first/last glyphs right. */ + if (i_run == last_run - 1 && remaing_offset < (last_run - 1)->len) { + run.len = remaing_offset - 1; + + if (!UniscribeShapeRun(this->text_buffer, run)) return NULL; + } + if (i_run == start_run && this->cur_range_offset > 0) { + assert(run.len - this->cur_range_offset > 0); + run.pos += this->cur_range_offset; + run.len -= this->cur_range_offset; + + if (!UniscribeShapeRun(this->text_buffer, run)) return NULL; + } + + *line->Append() = new UniscribeVisualRun(run, cur_pos); + cur_pos += run.total_advance; + } + + if (remaing_offset < (last_run - 1)->len) { + /* We didn't use up all of the last run, store remainder for the next line. */ + this->cur_range_offset = remaing_offset - 1; + this->cur_range = last_run - 1; + assert(this->cur_range->len > this->cur_range_offset); + } else { + this->cur_range_offset = 0; + this->cur_range = last_run; + } + + return line; +} + +/** + * Get the height of the line. + * @return The maximum height of the line. + */ +int UniscribeParagraphLayout::UniscribeLine::GetLeading() const +{ + int leading = 0; + for (const UniscribeVisualRun * const *run = this->Begin(); run != this->End(); run++) { + leading = max(leading, (*run)->GetLeading()); + } + + return leading; +} + +/** + * Get the width of this line. + * @return The width of the line. + */ +int UniscribeParagraphLayout::UniscribeLine::GetWidth() const +{ + int length = 0; + for (const UniscribeVisualRun * const *run = this->Begin(); run != this->End(); run++) { + length += (*run)->GetAdvance(); + } + + return length; +} + +UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(const UniscribeRun &range, int x) : glyphs(range.ft_glyphs), char_to_glyph(range.char_to_glyph), start_pos(range.pos), total_advance(range.total_advance), font(range.font) +{ + this->num_glyphs = (int)glyphs.size(); + this->positions.resize(this->num_glyphs * 2 + 2); + + int advance = 0; + for (int i = 0; i < this->num_glyphs; i++) { + this->positions[i * 2 + 0] = range.offsets[i].du + advance + x; + this->positions[i * 2 + 1] = range.offsets[i].dv; + + advance += range.advances[i]; + } + this->positions[this->num_glyphs * 2] = advance + x; +} + +const int *UniscribeParagraphLayout::UniscribeVisualRun::GetGlyphToCharMap() const +{ + if (this->glyph_to_char == NULL) { + this->glyph_to_char = CallocT(this->GetGlyphCount()); + + /* The char to glyph array contains the first glyph index of the cluster that is associated + * with each character. It is possible for a cluster to be formed of several chars. */ + for (int c = 0; c < (int)this->char_to_glyph.size(); c++) { + /* If multiple chars map to one glyph, only refer back to the first character. */ + if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos; + } + + /* We only marked the first glyph of each cluster in the loop above. Fill the gaps. */ + int last_char = this->glyph_to_char[0]; + for (int g = 0; g < this->GetGlyphCount(); g++) { + if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g]; + this->glyph_to_char[g] = last_char; + } + } + + return this->glyph_to_char; +} + + +/* virtual */ void UniscribeStringIterator::SetString(const char *s) +{ + const char *string_base = s; + + this->utf16_to_utf8.clear(); + this->str_info.clear(); + this->cur_pos = 0; + + /* Uniscribe operates on UTF-16, thus we have to convert the input string. + * To be able to return proper offsets, we have to create a mapping at the same time. */ + std::vector utf16_str; ///< UTF-16 copy of the string. + while (*s != '\0') { + size_t idx = s - string_base; + + WChar c = Utf8Consume(&s); + if (c < 0x10000) { + utf16_str.push_back((wchar_t)c); + } else { + /* Make a surrogate pair. */ + utf16_str.push_back((wchar_t)(0xD800 + ((c - 0x10000) >> 10))); + utf16_str.push_back((wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF))); + this->utf16_to_utf8.push_back(idx); + } + this->utf16_to_utf8.push_back(idx); + } + this->utf16_to_utf8.push_back(s - string_base); + + /* Query Uniscribe for word and cluster break information. */ + this->str_info.resize(utf16_to_utf8.size()); + + if (utf16_str.size() > 0) { + /* Itemize string into language runs. */ + std::vector runs = UniscribeItemizeString(&utf16_str[0], (int32)utf16_str.size()); + + for (std::vector::const_iterator run = runs.begin(); runs.size() > 0 && run != runs.end() - 1; run++) { + /* Get information on valid word and character break.s */ + int len = (run + 1)->iCharPos - run->iCharPos; + std::vector attr(len); + ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]); + + /* Extract the information we're interested in. */ + for (size_t c = 0; c < attr.size(); c++) { + /* First character of a run is always a valid word break. */ + this->str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0; + this->str_info[c + run->iCharPos].char_stop = attr[c].fCharStop; + } + } + } + + /* End-of-string is always a valid stopping point. */ + this->str_info.back().char_stop = true; + this->str_info.back().word_stop = true; +} + +/* virtual */ size_t UniscribeStringIterator::SetCurPosition(size_t pos) +{ + /* Convert incoming position to an UTF-16 string index. */ + size_t utf16_pos = 0; + for (size_t i = 0; i < this->utf16_to_utf8.size(); i++) { + if (this->utf16_to_utf8[i] == pos) { + utf16_pos = i; + break; + } + } + + /* Sanitize in case we get a position inside a grapheme cluster. */ + while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--; + this->cur_pos = utf16_pos; + + return this->utf16_to_utf8[this->cur_pos]; +} + +/* virtual */ size_t UniscribeStringIterator::Next(IterType what) +{ + assert(this->cur_pos <= this->utf16_to_utf8.size()); + assert(what == StringIterator::ITER_CHARACTER || what == StringIterator::ITER_WORD); + + if (this->cur_pos == this->utf16_to_utf8.size()) return END; + + do { + this->cur_pos++; + } while (this->cur_pos < this->utf16_to_utf8.size() && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop)); + + return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos]; +} + +/*virtual */ size_t UniscribeStringIterator::Prev(IterType what) +{ + assert(this->cur_pos <= this->utf16_to_utf8.size()); + assert(what == StringIterator::ITER_CHARACTER || what == StringIterator::ITER_WORD); + + if (this->cur_pos == 0) return END; + + do { + this->cur_pos--; + } while (this->cur_pos > 0 && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop)); + + return this->utf16_to_utf8[this->cur_pos]; +} + +#endif /* defined(WITH_UNISCRIBE) */ diff --git a/src/os/windows/string_uniscribe.h b/src/os/windows/string_uniscribe.h new file mode 100644 index 0000000000..6af858a88f --- /dev/null +++ b/src/os/windows/string_uniscribe.h @@ -0,0 +1,92 @@ +/* $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 . + */ + +/** @file string_uniscribe.h Functions related to laying out text on Win32. */ + +#ifndef STRING_UNISCRIBE_H +#define STRING_UNISCRIBE_H + +#if defined(WITH_UNISCRIBE) + +#include "../../gfx_layout.h" +#include "../../string_base.h" +#include + + +void UniscribeResetScriptCache(FontSize size); + + +/** + * Helper class to construct a new #UniscribeParagraphLayout. + */ +class UniscribeParagraphLayoutFactory { +public: + /** Helper for GetLayouter, to get the right type. */ + typedef wchar_t CharType; + /** Helper for GetLayouter, to get whether the layouter supports RTL. */ + static const bool SUPPORTS_RTL = true; + + /** + * Get the actual ParagraphLayout for the given buffer. + * @param buff The begin of the buffer. + * @param buff_end The location after the last element in the buffer. + * @param fontMapping THe mapping of the fonts. + * @return The ParagraphLayout instance. + */ + static ParagraphLayouter *GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &fontMapping); + + /** + * Append a wide character to the internal buffer. + * @param buff The buffer to append to. + * @param buffer_last The end of the buffer. + * @param c The character to add. + * @return The number of buffer spaces that were used. + */ + static size_t AppendToBuffer(CharType *buff, const CharType *buffer_last, WChar c) + { + if (c >= 0x010000U) { + /* Character is encoded using surrogates in UTF-16. */ + if (buff + 1 <= buffer_last) { + buff[0] = (CharType)(((c - 0x010000U) >> 10) + 0xD800); + buff[1] = (CharType)(((c - 0x010000U) & 0x3FF) + 0xDC00); + } else { + /* Not enough space in buffer. */ + *buff = 0; + } + return 2; + } else { + *buff = (CharType)(c & 0xFFFF); + return 1; + } + } +}; + +/** String iterator using Uniscribe as a backend. */ +class UniscribeStringIterator : public StringIterator { + /** */ + struct CharInfo { + bool word_stop : 1; ///< Code point is suitable as a word break. + bool char_stop : 1; ///< Code point is the start of a grapheme cluster, i.e. a "character". + }; + + std::vector str_info; ///< Break information for each code point. + std::vector utf16_to_utf8; ///< Mapping from UTF-16 code point position to index in the UTF-8 source string. + + size_t cur_pos; ///< Current iteration position. + +public: + virtual void SetString(const char *s); + virtual size_t SetCurPosition(size_t pos); + virtual size_t Next(IterType what); + virtual size_t Prev(IterType what); +}; + +#endif /* defined(WITH_UNISCRIBE) */ + +#endif /* STRING_UNISCRIBE_H */ diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index 8a90db4f7e..2d853037f0 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -28,6 +28,7 @@ #include "../../crashlog.h" #include #include +#include "../../language.h" /* Due to TCHAR, strncat and strncpy have to remain (for a while). */ #include "../../safeguards.h" @@ -739,6 +740,70 @@ uint GetCPUCoreCount() return info.dwNumberOfProcessors; } + +static WCHAR _cur_iso_locale[16] = L""; + +void Win32SetCurrentLocaleName(const char *iso_code) +{ + /* Convert the iso code into the format that windows expects. */ + char iso[16]; + if (strcmp(iso_code, "zh_TW") == 0) { + strecpy(iso, "zh-Hant", lastof(iso)); + } else if (strcmp(iso_code, "zh_CN") == 0) { + strecpy(iso, "zh-Hans", lastof(iso)); + } else { + /* Windows expects a '-' between language and country code, but we use a '_'. */ + strecpy(iso, iso_code, lastof(iso)); + for (char *c = iso; *c != '\0'; c++) { + if (*c == '_') *c = '-'; + } + } + + MultiByteToWideChar(CP_UTF8, 0, iso, -1, _cur_iso_locale, lengthof(_cur_iso_locale)); +} + +int OTTDStringCompare(const char *s1, const char *s2) +{ + typedef int (WINAPI *PFNCOMPARESTRINGEX)(LPCWSTR, DWORD, LPCWCH, int, LPCWCH, int, LPVOID, LPVOID, LPARAM); + static PFNCOMPARESTRINGEX _CompareStringEx = NULL; + static bool first_time = true; + +#ifndef SORT_DIGITSASNUMBERS +# define SORT_DIGITSASNUMBERS 0x00000008 // use digits as numbers sort method +#endif +#ifndef LINGUISTIC_IGNORECASE +# define LINGUISTIC_IGNORECASE 0x00000010 // linguistically appropriate 'ignore case' +#endif + + if (first_time) { + _CompareStringEx = (PFNCOMPARESTRINGEX)GetProcAddress(GetModuleHandle(_T("Kernel32")), "CompareStringEx"); + first_time = false; + } + + if (_CompareStringEx != NULL) { + /* CompareStringEx takes UTF-16 strings, even in ANSI-builds. */ + int len_s1 = MultiByteToWideChar(CP_UTF8, 0, s1, -1, NULL, 0); + int len_s2 = MultiByteToWideChar(CP_UTF8, 0, s2, -1, NULL, 0); + + if (len_s1 != 0 && len_s2 != 0) { + LPWSTR str_s1 = AllocaM(WCHAR, len_s1); + LPWSTR str_s2 = AllocaM(WCHAR, len_s2); + + MultiByteToWideChar(CP_UTF8, 0, s1, -1, str_s1, len_s1); + MultiByteToWideChar(CP_UTF8, 0, s2, -1, str_s2, len_s2); + + int result = _CompareStringEx(_cur_iso_locale, LINGUISTIC_IGNORECASE | SORT_DIGITSASNUMBERS, str_s1, -1, str_s2, -1, NULL, NULL, 0); + if (result != 0) return result; + } + } + + TCHAR s1_buf[512], s2_buf[512]; + convert_to_fs(s1, s1_buf, lengthof(s1_buf)); + convert_to_fs(s2, s2_buf, lengthof(s2_buf)); + + return CompareString(MAKELCID(_current_language->winlangid, SORT_DEFAULT), NORM_IGNORECASE, s1_buf, -1, s2_buf, -1); +} + #ifdef _MSC_VER /* Code from MSDN: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */ const DWORD MS_VC_EXCEPTION = 0x406D1388; diff --git a/src/os/windows/win32.h b/src/os/windows/win32.h index 16632f6e9a..4f813c4a6f 100644 --- a/src/os/windows/win32.h +++ b/src/os/windows/win32.h @@ -45,4 +45,7 @@ void SetWin32ThreadName(DWORD dwThreadID, const char* threadName); static inline void SetWin32ThreadName(DWORD dwThreadID, const char* threadName) {} #endif +void Win32SetCurrentLocaleName(const char *iso_code); +int OTTDStringCompare(const char *s1, const char *s2); + #endif /* WIN32_H */ diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index d9a70b7fca..70f148de38 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -73,7 +73,8 @@ struct CFollowTrackT inline void Init(Owner o, RailTypes railtype_override, CPerformanceTimer *pPerf) { - assert((!IsRoadTT() || m_veh != NULL) && (!IsRailTT() || railtype_override != INVALID_RAILTYPES)); + assert(!IsRoadTT() || m_veh != NULL); + assert(!IsRailTT() || railtype_override != INVALID_RAILTYPES); m_veh_owner = o; m_pPerf = pPerf; /* don't worry, all is inlined so compiler should remove unnecessary initializations */ @@ -226,8 +227,6 @@ protected: m_is_station = true; } else if (IsRoadTT() && IsRoadStopTile(m_new_tile)) { m_is_station = true; - } else { - m_is_station = false; } } @@ -240,7 +239,7 @@ protected: } else { m_new_td_bits = TrackStatusToTrackdirBits(GetTileTrackStatus(m_new_tile, TT(), IsRoadTT() ? RoadVehicle::From(m_veh)->compatible_roadtypes : 0)); - if (IsTram() && m_new_td_bits == 0) { + if (IsTram() && m_new_td_bits == TRACKDIR_BIT_NONE) { /* GetTileTrackStatus() returns 0 for single tram bits. * As we cannot change it there (easily) without breaking something, change it here */ switch (GetSingleTramBit(m_new_tile)) { diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index f989ff2c34..3218d8314f 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -807,7 +807,7 @@ static TrackdirBits GetDriveableTrackdirBits(TileIndex dst_tile, Trackdir src_tr { TrackdirBits trackdirbits = TrackStatusToTrackdirBits(GetTileTrackStatus(dst_tile, type, subtype)); - if (trackdirbits == 0 && type == TRANSPORT_ROAD && HasBit(subtype, ROADTYPE_TRAM)) { + if (trackdirbits == TRACKDIR_BIT_NONE && type == TRANSPORT_ROAD && HasBit(subtype, ROADTYPE_TRAM)) { /* GetTileTrackStatus() returns 0 for single tram bits. * As we cannot change it there (easily) without breaking something, change it here */ switch (GetSingleTramBit(dst_tile)) { @@ -900,7 +900,7 @@ static void NPFFollowTrack(AyStar *aystar, OpenListNode *current) trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype); - if (trackdirbits == 0) { + if (trackdirbits == TRACKDIR_BIT_NONE) { /* We cannot enter the next tile. Road vehicles can reverse, others reach dead end */ if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return; @@ -924,7 +924,7 @@ static void NPFFollowTrack(AyStar *aystar, OpenListNode *current) /* Enumerate possible track */ uint i = 0; - while (trackdirbits != 0) { + while (trackdirbits != TRACKDIR_BIT_NONE) { Trackdir dst_trackdir = RemoveFirstTrackdir(&trackdirbits); DEBUG(npf, 5, "Expanded into trackdir: %d, remaining trackdirs: 0x%X", dst_trackdir, trackdirbits); diff --git a/src/pathfinder/opf/opf_ship.cpp b/src/pathfinder/opf/opf_ship.cpp index 023c6a4a09..1a866337c7 100644 --- a/src/pathfinder/opf/opf_ship.cpp +++ b/src/pathfinder/opf/opf_ship.cpp @@ -183,9 +183,14 @@ bad:; } /** - * returns the track to choose on the next tile, or -1 when it's better to - * reverse. The tile given is the tile we are about to enter, enterdir is the - * direction in which we are entering the tile + * Finds the best track to choose on the next tile and + * returns INVALID_TRACK when it is better to reverse. + * @param v The ship. + * @param tile The tile we are about to enter. + * @param enterdir The direction entering the tile. + * @param tracks The tracks available on new tile. + * @param[out] path_found Whether a path has been found. + * @return Best track on next tile or INVALID_TRACK when better to reverse. */ Track OPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found) { @@ -195,13 +200,15 @@ Track OPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, Track track; /* Let's find out how far it would be if we would reverse first */ - Trackdir trackdir = v->GetVehicleTrackdir(); - TrackBits b = TrackStatusToTrackBits(GetTileTrackStatus(tile2, TRANSPORT_WATER, 0)) & DiagdirReachesTracks(ReverseDiagDir(enterdir)) & TrackdirBitsToTrackBits(TrackdirToTrackdirBits(trackdir)); + uint rev_dist = UINT_MAX; // distance if we reverse + Track cur_track = TrackdirToTrack(v->GetVehicleTrackdir()); // track on the current tile + DiagDirection rev_enterdir = ReverseDiagDir(enterdir); + TrackBits rev_tracks = TrackStatusToTrackBits(GetTileTrackStatus(tile2, TRANSPORT_WATER, 0)) & + DiagdirReachesTracks(rev_enterdir); - uint distr = UINT_MAX; // distance if we reversed - if (b != 0) { - distr = FindShipTrack(v, tile2, ReverseDiagDir(enterdir), b, tile, &track); - if (distr != UINT_MAX) distr++; // penalty for reversing + if (HasTrack(rev_tracks, cur_track)) { + rev_dist = FindShipTrack(v, tile2, rev_enterdir, TrackToTrackBits(cur_track), tile, &track); + if (rev_dist != UINT_MAX) rev_dist++; // penalty for reversing } /* And if we would not reverse? */ @@ -209,6 +216,6 @@ Track OPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, /* Due to the way this pathfinder works we cannot determine whether we're lost or not. */ path_found = true; - if (dist <= distr) return track; + if (dist <= rev_dist) return track; return INVALID_TRACK; // We could better reverse } diff --git a/src/pathfinder/yapf/yapf_common.hpp b/src/pathfinder/yapf/yapf_common.hpp index 660c231161..8ff69b3f6b 100644 --- a/src/pathfinder/yapf/yapf_common.hpp +++ b/src/pathfinder/yapf/yapf_common.hpp @@ -142,8 +142,7 @@ public: /** Called by YAPF to detect if node ends in the desired destination */ inline bool PfDetectDestination(Node &n) { - bool bDest = (n.m_key.m_tile == m_destTile) && ((m_destTrackdirs & TrackdirToTrackdirBits(n.GetTrackdir())) != TRACKDIR_BIT_NONE); - return bDest; + return (n.m_key.m_tile == m_destTile) && HasTrackdir(m_destTrackdirs, n.GetTrackdir()); } /** diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index ca317f09a7..022b9a1678 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -104,7 +104,7 @@ public: assert(IsValidTrackdir(td2)); int cost = 0; if (TrackFollower::Allow90degTurns() - && ((TrackdirToTrackdirBits(td2) & (TrackdirBits)TrackdirCrossesTrackdirs(td1)) != 0)) { + && HasTrackdir(TrackdirCrossesTrackdirs(td1), td2)) { /* 90-deg curve penalty */ cost += Yapf().PfGetSettings().rail_curve90_penalty; } else if (td2 != NextTrackdir(td1)) { @@ -280,7 +280,7 @@ public: { assert(!n.flags_u.flags_s.m_targed_seen); assert(tf->m_new_tile == n.m_key.m_tile); - assert((TrackdirToTrackdirBits(n.m_key.m_td) & tf->m_new_td_bits) != TRACKDIR_BIT_NONE); + assert((HasTrackdir(tf->m_new_td_bits, n.m_key.m_td))); CPerfStart perf_cost(Yapf().m_perf_cost); diff --git a/src/pathfinder/yapf/yapf_destrail.hpp b/src/pathfinder/yapf/yapf_destrail.hpp index 03519b059f..1d1833fbfd 100644 --- a/src/pathfinder/yapf/yapf_destrail.hpp +++ b/src/pathfinder/yapf/yapf_destrail.hpp @@ -166,16 +166,13 @@ public: /** Called by YAPF to detect if node ends in the desired destination */ inline bool PfDetectDestination(TileIndex tile, Trackdir td) { - bool bDest; if (m_dest_station_id != INVALID_STATION) { - bDest = HasStationTileRail(tile) + return HasStationTileRail(tile) && (GetStationIndex(tile) == m_dest_station_id) && (GetRailStationTrack(tile) == TrackdirToTrack(td)); - } else { - bDest = (tile == m_destTile) - && ((m_destTrackdirs & TrackdirToTrackdirBits(td)) != TRACKDIR_BIT_NONE); } - return bDest; + + return (tile == m_destTile) && HasTrackdir(m_destTrackdirs, td); } /** diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp index 380b641da7..edc8d2a3a3 100644 --- a/src/pathfinder/yapf/yapf_road.cpp +++ b/src/pathfinder/yapf/yapf_road.cpp @@ -185,8 +185,7 @@ public: /** Called by YAPF to detect if node ends in the desired destination */ inline bool PfDetectDestination(Node &n) { - bool bDest = IsRoadDepotTile(n.m_segment_last_tile); - return bDest; + return IsRoadDepotTile(n.m_segment_last_tile); } inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir) @@ -261,7 +260,7 @@ public: (m_non_artic || IsDriveThroughStopTile(tile)); } - return tile == m_destTile && ((m_destTrackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE); + return tile == m_destTile && HasTrackdir(m_destTrackdirs, trackdir); } /** @@ -421,7 +420,7 @@ public: /* set origin (tile, trackdir) */ TileIndex src_tile = v->tile; Trackdir src_td = v->GetVehicleTrackdir(); - if ((TrackStatusToTrackdirBits(GetTileTrackStatus(src_tile, TRANSPORT_ROAD, v->compatible_roadtypes)) & TrackdirToTrackdirBits(src_td)) == 0) { + if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(src_tile, TRANSPORT_ROAD, v->compatible_roadtypes)), src_td)) { /* sometimes the roadveh is not on the road (it resides on non-existing track) * how should we handle that situation? */ return false; @@ -504,7 +503,7 @@ FindDepotData YapfRoadVehicleFindNearestDepot(const RoadVehicle *v, int max_dist { TileIndex tile = v->tile; Trackdir trackdir = v->GetVehicleTrackdir(); - if ((TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes)) & TrackdirToTrackdirBits(trackdir)) == 0) { + if (!HasTrackdir(TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, v->compatible_roadtypes)), trackdir)) { return FindDepotData(); } diff --git a/src/pathfinder/yapf/yapf_ship.cpp b/src/pathfinder/yapf/yapf_ship.cpp index df6cd468ce..44a5c0cfa7 100644 --- a/src/pathfinder/yapf/yapf_ship.cpp +++ b/src/pathfinder/yapf/yapf_ship.cpp @@ -65,7 +65,7 @@ public: /* use vehicle's current direction if that's possible, otherwise use first usable one. */ Trackdir veh_dir = v->GetVehicleTrackdir(); - return ((trackdirs & TrackdirToTrackdirBits(veh_dir)) != 0) ? veh_dir : (Trackdir)FindFirstBit2x64(trackdirs); + return (HasTrackdir(trackdirs, veh_dir)) ? veh_dir : (Trackdir)FindFirstBit2x64(trackdirs); } /* move back to the old tile/trackdir (where ship is coming from) */ diff --git a/src/pbs.cpp b/src/pbs.cpp index 133293909f..6bb35a6964 100644 --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -81,7 +81,7 @@ void SetRailStationPlatformReservation(TileIndex start, DiagDirection dir, bool */ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations) { - assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0); + assert(HasTrack(TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)), t)); if (_settings_client.gui.show_track_reservation) { /* show the reserved rail if needed */ @@ -142,7 +142,7 @@ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations) */ void UnreserveRailTrack(TileIndex tile, Track t) { - assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0); + assert(HasTrack(TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)), t)); if (_settings_client.gui.show_track_reservation) { if (IsBridgeTile(tile)) { diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 73fe29da0d..de8d434741 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -1908,7 +1908,7 @@ static void SetDefaultRailGui() RailType rt = (RailType)(_settings_client.gui.default_rail_type + RAILTYPE_END); if (rt == DEF_RAILTYPE_MOST_USED) { /* Find the most used rail type */ - RailType count[RAILTYPE_END]; + uint count[RAILTYPE_END]; memset(count, 0, sizeof(count)); for (TileIndex t = 0; t < MapSize(); t++) { if (IsTileType(t, MP_RAILWAY) || IsLevelCrossingTile(t) || HasStationTileRail(t) || diff --git a/src/rev.cpp.in b/src/rev.cpp.in index 6689fe6891..0f4b2a6b57 100644 --- a/src/rev.cpp.in +++ b/src/rev.cpp.in @@ -64,13 +64,13 @@ const byte _openttd_revision_modified = !!MODIFIED!!; * 24-27 minor version * 20-23 build * 19 1 if it is a release, 0 if it is not. - * 0-18 used to be the SVN revision, currently unused + * 0-18 used to be the SVN revision, now just last revision before switch to git * * The 19th bit is there so the development/betas/alpha, etc. leading to a * final release will always have a lower version number than the released * version, thus making comparisons on specific revisions easy. */ -const uint32 _openttd_newgrf_version = 1 << 28 | 9 << 24 | 0 << 20 | 0 << 19; +const uint32 _openttd_newgrf_version = 1 << 28 | 9 << 24 | 0 << 20 | 0 << 19 | 28004; #ifdef __MORPHOS__ /** diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 801d334351..6faa422627 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -633,24 +633,28 @@ struct BuildRoadToolbarWindow : Window { break; case DDSP_BUILD_BUSSTOP: - PlaceRoadStop(start_tile, end_tile, (_ctrl_pressed << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_BUS, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_BUS])); + case DDSP_REMOVE_BUSSTOP: + if (this->IsWidgetLowered(WID_ROT_BUS_STATION)) { + if (_remove_button_clicked) { + TileArea ta(start_tile, end_tile); + DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_BUS, CMD_REMOVE_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_station[ROADSTOP_BUS]), CcPlaySound_SPLAT_OTHER); + } else { + PlaceRoadStop(start_tile, end_tile, (_ctrl_pressed << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_BUS, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_BUS])); + } + } break; case DDSP_BUILD_TRUCKSTOP: - PlaceRoadStop(start_tile, end_tile, (_ctrl_pressed << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_TRUCK, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_TRUCK])); + case DDSP_REMOVE_TRUCKSTOP: + if (this->IsWidgetLowered(WID_ROT_TRUCK_STATION)) { + if (_remove_button_clicked) { + TileArea ta(start_tile, end_tile); + DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_TRUCK, CMD_REMOVE_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_station[ROADSTOP_TRUCK]), CcPlaySound_SPLAT_OTHER); + } else { + PlaceRoadStop(start_tile, end_tile, (_ctrl_pressed << 5) | RoadTypeToRoadTypes(_cur_roadtype) << 2 | ROADSTOP_TRUCK, CMD_BUILD_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_build_station[ROADSTOP_TRUCK])); + } + } break; - - case DDSP_REMOVE_BUSSTOP: { - TileArea ta(start_tile, end_tile); - DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_BUS, CMD_REMOVE_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_station[ROADSTOP_BUS]), CcPlaySound_SPLAT_OTHER); - break; - } - - case DDSP_REMOVE_TRUCKSTOP: { - TileArea ta(start_tile, end_tile); - DoCommandP(ta.tile, ta.w | ta.h << 8, (_ctrl_pressed << 1) | ROADSTOP_TRUCK, CMD_REMOVE_ROAD_STOP | CMD_MSG(_road_type_infos[_cur_roadtype].err_remove_station[ROADSTOP_TRUCK]), CcPlaySound_SPLAT_OTHER); - break; - } } } } diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp index 6fa20727c2..edc2c4f7ee 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -351,7 +351,8 @@ static const SaveLoad _company_economy_desc[] = { SLE_CONDVAR(CompanyEconomyEntry, company_value, SLE_INT64, 2, SL_MAX_VERSION), SLE_CONDVAR(CompanyEconomyEntry, delivered_cargo[NUM_CARGO - 1], SLE_INT32, 0, 169), - SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, NUM_CARGO, 170, SL_MAX_VERSION), + SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, 32, 170, 198), + SLE_CONDARR(CompanyEconomyEntry, delivered_cargo, SLE_UINT32, NUM_CARGO, 199, SL_MAX_VERSION), SLE_VAR(CompanyEconomyEntry, performance_history, SLE_INT32), SLE_END() @@ -499,7 +500,6 @@ static void Check_PLYR() int index; while ((index = SlIterateArray()) != -1) { CompanyProperties *cprops = new CompanyProperties(); - memset(cprops, 0, sizeof(*cprops)); SaveLoad_PLYR_common(NULL, cprops); /* We do not load old custom names */ diff --git a/src/saveload/economy_sl.cpp b/src/saveload/economy_sl.cpp index 0bc5ff4bd2..d1235f0d36 100644 --- a/src/saveload/economy_sl.cpp +++ b/src/saveload/economy_sl.cpp @@ -29,7 +29,7 @@ static void Load_PRIC() /** Cargo payment rates in pre 126 savegames */ static void Load_CAPR() { - uint num_cargo = IsSavegameVersionBefore(55) ? 12 : NUM_CARGO; + uint num_cargo = IsSavegameVersionBefore(55) ? 12 : IsSavegameVersionBefore(199) ? 32 : NUM_CARGO; int vt = IsSavegameVersionBefore(65) ? SLE_FILE_I32 : SLE_FILE_I64; SlArray(NULL, num_cargo, vt | SLE_VAR_NULL); SlArray(NULL, num_cargo, SLE_FILE_U16 | SLE_VAR_NULL); diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index a0e0c76123..39c5d30886 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -272,8 +272,9 @@ * 196 27778 1.7.x * 197 27978 1.8.x * 198 + * 199 */ -extern const uint16 SAVEGAME_VERSION = 198; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 199; ///< Current savegame version of OpenTTD. const uint16 SAVEGAME_VERSION_EXT = 0x8000; ///< Savegame extension indicator mask SavegameType _savegame_type; ///< type of savegame we are loading diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 6934b82ef3..cc15709597 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -571,7 +571,7 @@ typedef SaveLoad SaveLoadGlobVarList; * @param variable Name of the global variable. * @param type Storage of the data in memory and in the savegame. */ -#define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, lengthof(variable), 0, SL_MAX_VERSION) +#define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, sizeof(variable), 0, SL_MAX_VERSION) /** * Storage of a global list in every savegame version. diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp index 391ba30a8e..f01123da0a 100644 --- a/src/saveload/station_sl.cpp +++ b/src/saveload/station_sl.cpp @@ -329,6 +329,7 @@ static void Load_STNS() _cargo_days = 0; _cargo_feeder_share = 0; + uint num_cargo = IsSavegameVersionBefore(55) ? 12 : IsSavegameVersionBefore(199) ? 32 : NUM_CARGO; int index; while ((index = SlIterateArray()) != -1) { Station *st = new (index) Station(); @@ -337,7 +338,6 @@ static void Load_STNS() _waiting_acceptance = 0; - uint num_cargo = IsSavegameVersionBefore(55) ? 12 : NUM_CARGO; for (CargoID i = 0; i < num_cargo; i++) { GoodsEntry *ge = &st->goods[i]; SlObject(ge, GetGoodsDesc()); @@ -377,10 +377,11 @@ static void Ptrs_STNS() /* Don't run when savegame version is higher than or equal to 123. */ if (!IsSavegameVersionBefore(123)) return; + uint num_cargo = IsSavegameVersionBefore(199) ? 32 : NUM_CARGO; Station *st; FOR_ALL_STATIONS(st) { if (!IsSavegameVersionBefore(68)) { - for (CargoID i = 0; i < NUM_CARGO; i++) { + for (CargoID i = 0; i < num_cargo; i++) { GoodsEntry *ge = &st->goods[i]; SwapPackets(ge); SlObject(ge, GetGoodsDesc()); @@ -440,7 +441,8 @@ static const SaveLoad _station_desc[] = { SLE_VAR(Station, last_vehicle_type, SLE_UINT8), SLE_VAR(Station, had_vehicle_of_type, SLE_UINT8), SLE_LST(Station, loading_vehicles, REF_VEHICLE), - SLE_CONDVAR(Station, always_accepted, SLE_UINT32, 127, SL_MAX_VERSION), + SLE_CONDVAR(Station, always_accepted, SLE_FILE_U32 | SLE_VAR_U64, 127, 198), + SLE_CONDVAR(Station, always_accepted, SLE_UINT64, 199, SL_MAX_VERSION), SLE_END() }; @@ -520,6 +522,7 @@ static void Load_STNN() { _num_flows = 0; + uint num_cargo = IsSavegameVersionBefore(199) ? 32 : NUM_CARGO; int index; while ((index = SlIterateArray()) != -1) { bool waypoint = (SlReadByte() & FACIL_WAYPOINT) != 0; @@ -538,7 +541,7 @@ static void Load_STNN() memcpy(st->airport.psa->storage, _old_st_persistent_storage.storage, sizeof(st->airport.psa->storage)); } - for (CargoID i = 0; i < NUM_CARGO; i++) { + for (CargoID i = 0; i < num_cargo; i++) { SlObject(&st->goods[i], GetGoodsDesc()); FlowSaveLoad flow; FlowStat *fs = NULL; @@ -580,9 +583,10 @@ static void Ptrs_STNN() /* Don't run when savegame version lower than 123. */ if (IsSavegameVersionBefore(123)) return; + uint num_cargo = IsSavegameVersionBefore(199) ? 32 : NUM_CARGO; Station *st; FOR_ALL_STATIONS(st) { - for (CargoID i = 0; i < NUM_CARGO; i++) { + for (CargoID i = 0; i < num_cargo; i++) { GoodsEntry *ge = &st->goods[i]; if (IsSavegameVersionBefore(183)) { SwapPackets(ge); diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp index 41ac701650..13438d85bf 100644 --- a/src/saveload/town_sl.cpp +++ b/src/saveload/town_sl.cpp @@ -192,7 +192,8 @@ static const SaveLoad _town_desc[] = { SLE_CONDLST(Town, psa_list, REF_STORAGE, 161, SL_MAX_VERSION), - SLE_CONDVAR(Town, cargo_produced, SLE_UINT32, 166, SL_MAX_VERSION), + SLE_CONDVAR(Town, cargo_produced, SLE_FILE_U32 | SLE_VAR_U64, 166, 198), + SLE_CONDVAR(Town, cargo_produced, SLE_UINT64, 199, SL_MAX_VERSION), /* reserve extra space in savegame here. (currently 30 bytes) */ SLE_CONDNULL(30, 2, SL_MAX_VERSION), @@ -274,12 +275,13 @@ static void Save_TOWN() static void Load_TOWN() { int index; + uint num_cargo = IsSavegameVersionBefore(199) ? 32 : NUM_CARGO; while ((index = SlIterateArray()) != -1) { Town *t = new (index) Town(); SlObject(t, _town_desc); - for (CargoID i = 0; i < NUM_CARGO; i++) { + for (CargoID i = 0; i < num_cargo; i++) { SlObject(&t->supplied[i], _town_supplied_desc); } for (int i = TE_BEGIN; i < TE_END; i++) { diff --git a/src/settings_type.h b/src/settings_type.h index fc4059c7bf..f9cc00f3a3 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -114,6 +114,7 @@ struct GUISettings { bool threaded_saves; ///< should we do threaded saves? bool keep_all_autosave; ///< name the autosave in a different way bool autosave_on_exit; ///< save an autosave when you quit the game, but do not ask "Do you really want to quit?" + bool autosave_on_network_disconnect; ///< save an autosave when you get disconnected from a network game with an error? uint8 date_format_in_default_names; ///< should the default savegame/screenshot name use long dates (31th Dec 2008), short dates (31-12-2008) or ISO dates (2008-12-31) byte max_num_autosaves; ///< controls how many autosavegames are made before the game starts to overwrite (names them 0 to max_num_autosaves - 1) bool population_in_label; ///< show the population of a town in his label? diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 967cd4e1d6..0bdcf7041a 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -537,6 +537,10 @@ static void ShipController(Ship *v) if (v->current_order.IsType(OT_LEAVESTATION)) { v->current_order.Free(); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); + /* Test if continuing forward would lead to a dead-end, moving into the dock. */ + DiagDirection exitdir = VehicleExitDir(v->direction, v->state); + TileIndex tile = TileAddByDiagDir(v->tile, exitdir); + if (TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0, exitdir)) == TRACK_BIT_NONE) goto reverse_direction; } else if (v->dest_tile != 0) { /* We have a target, let's see if we reached it... */ if (v->current_order.IsType(OT_GOTO_WAYPOINT) && diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 07780de13a..07db05856f 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -960,6 +960,8 @@ static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags num_roadbits += CountBits(GetRoadBits(cur_tile, ROADTYPE_ROAD)); } + if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_ONEWAY_ROAD); + /* There is a tram, check if we can build road+tram stop over it. */ if (HasBit(cur_rts, ROADTYPE_TRAM)) { Owner tram_owner = GetRoadOwner(cur_tile, ROADTYPE_TRAM); diff --git a/src/string.cpp b/src/string.cpp index 343250a979..bb74205c4d 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -25,6 +25,14 @@ #include // required by vsnprintf implementation for MSVC #endif +#ifdef WIN32 +#include "os/windows/win32.h" +#endif + +#ifdef WITH_UNISCRIBE +#include "os/windows/string_uniscribe.h" +#endif + #ifdef WITH_ICU_SORT /* Required by strnatcmp. */ #include @@ -571,20 +579,32 @@ int strnatcmp(const char *s1, const char *s2, bool ignore_garbage_at_front) s1 = SkipGarbage(s1); s2 = SkipGarbage(s2); } + #ifdef WITH_ICU_SORT if (_current_collator != NULL) { UErrorCode status = U_ZERO_ERROR; int result = _current_collator->compareUTF8(s1, s2, status); if (U_SUCCESS(status)) return result; } - #endif /* WITH_ICU_SORT */ +#if defined(WIN32) && !defined(STRGEN) && !defined(SETTINGSGEN) + int res = OTTDStringCompare(s1, s2); + if (res != 0) return res - 2; // Convert to normal C return values. +#endif + /* Do a normal comparison if ICU is missing or if we cannot create a collator. */ return strcasecmp(s1, s2); } -#ifdef WITH_ICU_SORT +#ifdef WITH_UNISCRIBE + +/* static */ StringIterator *StringIterator::Create() +{ + return new UniscribeStringIterator(); +} + +#elif defined(WITH_ICU_SORT) #include #include diff --git a/src/strings.cpp b/src/strings.cpp index fd45e6a0b8..b793503187 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1147,7 +1147,7 @@ static char *FormatString(char *buff, const char *str_arg, StringParameters *arg } case SCC_CARGO_LIST: { // {CARGO_LIST} - CargoTypes cmask = args->GetInt32(SCC_CARGO_LIST); + CargoTypes cmask = args->GetInt64(SCC_CARGO_LIST); bool first = true; const CargoSpec *cs; @@ -1788,6 +1788,11 @@ bool ReadLanguagePack(const LanguageMetadata *lang) strecpy(_config_language_file, c_file, lastof(_config_language_file)); SetCurrentGrfLangID(_current_language->newgrflangid); +#ifdef WIN32 + extern void Win32SetCurrentLocaleName(const char *iso_code); + Win32SetCurrentLocaleName(_current_language->isocode); +#endif + #ifdef WITH_ICU_SORT /* Delete previous collator. */ if (_current_collator != NULL) { @@ -2130,7 +2135,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher) /* Update the font with cache */ LoadStringWidthTable(searcher->Monospace()); -#if !defined(WITH_ICU_LAYOUT) +#if !defined(WITH_ICU_LAYOUT) && !defined(WITH_UNISCRIBE) /* * For right-to-left languages we need the ICU library. If * we do not have support for that library we warn the user diff --git a/src/table/settings.h.preamble b/src/table/settings.h.preamble index 8e53410de6..9adc7e79fa 100644 --- a/src/table/settings.h.preamble +++ b/src/table/settings.h.preamble @@ -77,7 +77,7 @@ static size_t ConvertLandscape(const char *value); SDTG_GENERAL(name, SDT_INTLIST, SL_ARR, type, flags, guiflags, var, length, def, 0, 0, 0, NULL, str, strhelp, strval, proc, from, to, cat, extver, patxname) #define SDTG_STR(name, type, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(name, SDT_STRING, SL_STR, type, flags, guiflags, var, lengthof(var), def, 0, 0, 0, NULL, str, strhelp, strval, proc, from, to, cat, extver, patxname) + SDTG_GENERAL(name, SDT_STRING, SL_STR, type, flags, guiflags, var, sizeof(var), def, 0, 0, 0, NULL, str, strhelp, strval, proc, from, to, cat, extver, patxname) #define SDTG_OMANY(name, type, flags, guiflags, var, def, max, full, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ SDTG_GENERAL(name, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, max, 0, full, str, strhelp, strval, proc, from, to, cat, extver, patxname) @@ -108,7 +108,7 @@ static size_t ConvertLandscape(const char *value); SDT_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, base, var, lengthof(((base*)8)->var), def, 0, 0, 0, NULL, str, strhelp, strval, proc, NULL, from, to, cat, extver, patxname) #define SDT_STR(base, var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDT_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, base, var, lengthof(((base*)8)->var), def, 0, 0, 0, NULL, str, strhelp, strval, proc, NULL, from, to, cat, extver, patxname) + SDT_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, base, var, sizeof(((base*)8)->var), def, 0, 0, 0, NULL, str, strhelp, strval, proc, NULL, from, to, cat, extver, patxname) #define SDT_CHR(base, var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ SDT_GENERAL(#var, SDT_STRING, SL_VAR, SLE_CHAR, flags, guiflags, base, var, 1, def, 0, 0, 0, NULL, str, strhelp, strval, proc, NULL, from, to, cat, extver, patxname) @@ -133,7 +133,7 @@ static size_t ConvertLandscape(const char *value); SDTG_GENERAL(#var, SDT_INTLIST, SL_ARR, type, flags, guiflags, _settings_client.var, lengthof(_settings_client.var), def, 0, 0, 0, NULL, str, strhelp, strval, proc, from, to, cat, extver, patxname) #define SDTC_STR(var, type, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, _settings_client.var, lengthof(_settings_client.var), def, 0, 0, 0, NULL, str, strhelp, strval, proc, from, to, cat, extver, patxname) + SDTG_GENERAL(#var, SDT_STRING, SL_STR, type, flags, guiflags, _settings_client.var, sizeof(_settings_client.var), def, 0, 0, 0, NULL, str, strhelp, strval, proc, from, to, cat, extver, patxname) #define SDTC_OMANY(var, type, flags, guiflags, def, max, full, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ SDTG_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, from, to, cat, extver, patxname) diff --git a/src/table/settings.ini b/src/table/settings.ini index 42e3c9db86..d1fb1f4224 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -2996,6 +2996,12 @@ flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC def = false cat = SC_BASIC +[SDTC_BOOL] +var = gui.autosave_on_network_disconnect +flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC +def = true +cat = SC_EXPERT + [SDTC_VAR] var = gui.max_num_autosaves type = SLE_UINT8 diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 0575b0a37b..92a3991ba9 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -1575,6 +1575,9 @@ void UpdateTownMaxPass(Town *t) t->supplied[CT_MAIL].old_max = t->cache.population >> 4; } +static void UpdateTownGrowthRate(Town *t); +static void UpdateTownGrowth(Town *t); + /** * Does the actual town creation. * @@ -1654,6 +1657,7 @@ static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize t->cache.num_houses -= x; UpdateTownRadius(t); + UpdateTownGrowthRate(t); UpdateTownMaxPass(t); UpdateAirportsNoise(); } @@ -2412,6 +2416,7 @@ static bool BuildTownHouse(Town *t, TileIndex tile) MakeTownHouse(tile, t, construction_counter, construction_stage, house, random_bits); UpdateTownRadius(t); + UpdateTownGrowthRate(t); UpdateTownCargoes(t, tile); return true; @@ -2547,8 +2552,6 @@ const CargoSpec *FindFirstCargoWithTownEffect(TownEffect effect) return NULL; } -static void UpdateTownGrowRate(Town *t); - /** * Change the cargo goal of a town. * @param tile Unused. @@ -2577,7 +2580,7 @@ CommandCost CmdTownCargoGoal(TileIndex tile, DoCommandFlag flags, uint32 p1, uin if (flags & DC_EXEC) { t->goal[te] = p2; - UpdateTownGrowRate(t); + UpdateTownGrowth(t); InvalidateWindowData(WC_TOWN_VIEW, index); } @@ -2627,7 +2630,7 @@ CommandCost CmdTownGrowthRate(TileIndex tile, DoCommandFlag flags, uint32 p1, ui if (flags & DC_EXEC) { if (p2 == 0) { - /* Just clear the flag, UpdateTownGrowRate will determine a proper growth rate */ + /* Just clear the flag, UpdateTownGrowth will determine a proper growth rate */ ClrBit(t->flags, TOWN_CUSTOM_GROWTH); } else { uint old_rate = t->growth_rate; @@ -2641,7 +2644,7 @@ CommandCost CmdTownGrowthRate(TileIndex tile, DoCommandFlag flags, uint32 p1, ui t->growth_rate = p2; SetBit(t->flags, TOWN_CUSTOM_GROWTH); } - UpdateTownGrowRate(t); + UpdateTownGrowth(t); InvalidateWindowData(WC_TOWN_VIEW, p1); } @@ -2928,7 +2931,7 @@ static CommandCost TownActionFundBuildings(Town *t, DoCommandFlag flags) t->fund_buildings_months = 3; /* Enable growth (also checking GameScript's opinion) */ - UpdateTownGrowRate(t); + UpdateTownGrowth(t); /* Build a new house, but add a small delay to make sure * that spamming funding doesn't let town grow any faster @@ -3132,8 +3135,87 @@ static void UpdateTownRating(Town *t) SetWindowDirty(WC_TOWN_AUTHORITY, t->index); } -static void UpdateTownGrowRate(Town *t) + +/** + * Updates town grow counter after growth rate change. + * Preserves relative house builting progress whenever it can. + * @param town The town to calculate grow counter for + * @param prev_growth_rate Town growth rate before it changed (one that was used with grow counter to be updated) + */ +static void UpdateTownGrowCounter(Town *t, uint16 prev_growth_rate) { + if (t->growth_rate == TOWN_GROWTH_RATE_NONE) return; + if (prev_growth_rate == TOWN_GROWTH_RATE_NONE) { + t->grow_counter = min(t->growth_rate, t->grow_counter); + return; + } + t->grow_counter = RoundDivSU((uint32)t->grow_counter * (t->growth_rate + 1), prev_growth_rate + 1); +} + +/** + * Calculates amount of active stations in the range of town (HZB_TOWN_EDGE). + * @param town The town to calculate stations for + * @returns Amount of active stations + */ +static int CountActiveStations(Town *t) +{ + int n = 0; + const Station *st; + FOR_ALL_STATIONS(st) { + if (DistanceSquare(st->xy, t->xy) <= t->cache.squared_town_zone_radius[0]) { + if (st->time_since_load <= 20 || st->time_since_unload <= 20) { + n++; + } + } + } + return n; +} + +/** + * Calculates town growth rate in normal conditions (custom growth rate not set). + * If town growth speed is set to None(0) returns the same rate as if it was Normal(2). + * @param town The town to calculate growth rate for + * @returns Calculated growth rate + */ +static uint GetNormalGrowthRate(Town *t) +{ + static const uint16 _grow_count_values[2][6] = { + { 120, 120, 120, 100, 80, 60 }, // Fund new buildings has been activated + { 320, 420, 300, 220, 160, 100 } // Normal values + }; + + int n = CountActiveStations(t); + uint16 m = _grow_count_values[t->fund_buildings_months != 0 ? 0 : 1][min(n, 5)]; + + uint growth_multiplier = _settings_game.economy.town_growth_rate != 0 ? _settings_game.economy.town_growth_rate - 1 : 1; + + m >>= growth_multiplier; + if (t->larger_town) m /= 2; + + return TownTicksToGameTicks(m / (t->cache.num_houses / 50 + 1)); +} + +/** + * Updates town growth rate. + * @param town The town to update growth rate for + */ +static void UpdateTownGrowthRate(Town *t) +{ + if (HasBit(t->flags, TOWN_CUSTOM_GROWTH)) return; + uint old_rate = t->growth_rate; + t->growth_rate = GetNormalGrowthRate(t); + UpdateTownGrowCounter(t, old_rate); + SetWindowDirty(WC_TOWN_VIEW, t->index); +} + +/** + * Updates town growth state (whether it is growing or not). + * @param town The town to update growth for + */ +static void UpdateTownGrowth(Town *t) +{ + UpdateTownGrowthRate(t); + ClrBit(t->flags, TOWN_IS_GROWING); SetWindowDirty(WC_TOWN_VIEW, t->index); @@ -3162,44 +3244,7 @@ static void UpdateTownGrowRate(Town *t) return; } - /** - * Towns are processed every TOWN_GROWTH_TICKS ticks, and this is the - * number of times towns are processed before a new building is built. - */ - static const uint16 _grow_count_values[2][6] = { - { 120, 120, 120, 100, 80, 60 }, // Fund new buildings has been activated - { 320, 420, 300, 220, 160, 100 } // Normal values - }; - - int n = 0; - - const Station *st; - FOR_ALL_STATIONS(st) { - if (DistanceSquare(st->xy, t->xy) <= t->cache.squared_town_zone_radius[0]) { - if (st->time_since_load <= 20 || st->time_since_unload <= 20) { - n++; - } - } - } - - uint16 m; - - if (t->fund_buildings_months != 0) { - m = _grow_count_values[0][min(n, 5)]; - } else { - m = _grow_count_values[1][min(n, 5)]; - if (n == 0 && !Chance16(1, 12)) return; - } - - /* Use the normal growth rate values if new buildings have been funded in - * this town and the growth rate is set to none. */ - uint growth_multiplier = _settings_game.economy.town_growth_rate != 0 ? _settings_game.economy.town_growth_rate - 1 : 1; - - m >>= growth_multiplier; - if (t->larger_town) m /= 2; - - t->growth_rate = TownTicksToGameTicks(m / (t->cache.num_houses / 50 + 1)); - t->grow_counter = min(t->growth_rate, t->grow_counter); + if (t->fund_buildings_months == 0 && CountActiveStations(t) == 0 && !Chance16(1, 12)) return; SetBit(t->flags, TOWN_IS_GROWING); SetWindowDirty(WC_TOWN_VIEW, t->index); @@ -3433,8 +3478,8 @@ void TownsMonthlyLoop() } UpdateTownAmounts(t); + UpdateTownGrowth(t); UpdateTownRating(t); - UpdateTownGrowRate(t); UpdateTownUnwanted(t); UpdateTownCargoes(t); } diff --git a/src/town_gui.cpp b/src/town_gui.cpp index 1d604634d3..17449854d6 100644 --- a/src/town_gui.cpp +++ b/src/town_gui.cpp @@ -337,13 +337,15 @@ public: SetDParam(1, this->town->cache.num_houses); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y, STR_TOWN_VIEW_POPULATION_HOUSES); - SetDParam(0, this->town->supplied[CT_PASSENGERS].old_act); - SetDParam(1, this->town->supplied[CT_PASSENGERS].old_max); - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX); + SetDParam(0, 1 << CT_PASSENGERS); + SetDParam(1, this->town->supplied[CT_PASSENGERS].old_act); + SetDParam(2, this->town->supplied[CT_PASSENGERS].old_max); + DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX); - SetDParam(0, this->town->supplied[CT_MAIL].old_act); - SetDParam(1, this->town->supplied[CT_MAIL].old_max); - DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX); + SetDParam(0, 1 << CT_MAIL); + SetDParam(1, this->town->supplied[CT_MAIL].old_act); + SetDParam(2, this->town->supplied[CT_MAIL].old_max); + DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_LEFT, y += FONT_HEIGHT_NORMAL, STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX); bool first = true; for (int i = TE_BEGIN; i < TE_END; i++) { @@ -745,6 +747,16 @@ public: } } + /** + * Get the string to draw the town name. + * @param t Town to draw. + * @return The string to use. + */ + static StringID GetTownString(const Town *t) + { + return t->larger_town ? STR_TOWN_DIRECTORY_CITY : STR_TOWN_DIRECTORY_TOWN; + } + virtual void DrawWidget(const Rect &r, int widget) const { switch (widget) { @@ -783,7 +795,7 @@ public: SetDParam(0, t->index); SetDParam(1, t->cache.population); - DrawString(text_left, text_right, y + (this->resize.step_height - FONT_HEIGHT_NORMAL) / 2, STR_TOWN_DIRECTORY_TOWN); + DrawString(text_left, text_right, y + (this->resize.step_height - FONT_HEIGHT_NORMAL) / 2, GetTownString(t)); y += this->resize.step_height; if (++n == this->vscroll->GetCapacity()) break; // max number of towns in 1 window @@ -822,7 +834,7 @@ public: SetDParam(0, t->index); SetDParamMaxDigits(1, 8); - d = maxdim(d, GetStringBoundingBox(STR_TOWN_DIRECTORY_TOWN)); + d = maxdim(d, GetStringBoundingBox(GetTownString(t))); } Dimension icon_size = GetSpriteSize(SPR_TOWN_RATING_GOOD); d.width += icon_size.width + 2; diff --git a/src/track_func.h b/src/track_func.h index 68de827ef1..8e2056265e 100644 --- a/src/track_func.h +++ b/src/track_func.h @@ -61,7 +61,7 @@ static inline bool IsValidTrackdirForRoadVehicle(Trackdir trackdir) */ static inline bool IsValidTrackdir(Trackdir trackdir) { - return (1 << trackdir & TRACKDIR_BIT_MASK) != 0; + return (1 << trackdir & TRACKDIR_BIT_MASK) != TRACKDIR_BIT_NONE; } /** @@ -331,6 +331,28 @@ static inline TrackdirBits TrackBitsToTrackdirBits(TrackBits bits) return (TrackdirBits)(bits * 0x101); } +/** + * Checks whether a TrackBits has a given Track. + * @param tracks The track bits. + * @param track The track to check. + */ +static inline bool HasTrack(TrackBits tracks, Track track) +{ + assert(IsValidTrack(track)); + return HasBit(tracks, track); +} + +/** + * Checks whether a TrackdirBits has a given Trackdir. + * @param trackdirs The trackdir bits. + * @param trackdir The trackdir to check. + */ +static inline bool HasTrackdir(TrackdirBits trackdirs, Trackdir trackdir) +{ + assert(IsValidTrackdir(trackdir)); + return HasBit(trackdirs, trackdir); +} + /** * Returns the present-trackdir-information of a TrackStatus. * @@ -692,4 +714,25 @@ static inline bool IsUphillTrackdir(Slope slope, Trackdir dir) return HasBit(_uphill_trackdirs[RemoveHalftileSlope(slope)], dir); } +/** + * Determine the side in which the vehicle will leave the tile + * + * @param direction vehicle direction + * @param track vehicle track bits + * @return side of tile the vehicle will leave + */ +static inline DiagDirection VehicleExitDir(Direction direction, TrackBits track) +{ + static const TrackBits state_dir_table[DIAGDIR_END] = { TRACK_BIT_RIGHT, TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER }; + + DiagDirection diagdir = DirToDiagDir(direction); + + /* Determine the diagonal direction in which we will exit this tile */ + if (!HasBit(direction, 0) && track != state_dir_table[diagdir]) { + diagdir = ChangeDiagDir(diagdir, DIAGDIRDIFF_90LEFT); + } + + return diagdir; +} + #endif /* TRACK_FUNC_H */ diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 9f4ecbea6e..daebb9789c 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -57,27 +57,6 @@ bool IsValidImageIndex(uint8 image_index) return image_index < lengthof(_engine_sprite_base); } -/** - * Determine the side in which the train will leave the tile - * - * @param direction vehicle direction - * @param track vehicle track bits - * @return side of tile the train will leave - */ -static inline DiagDirection TrainExitDir(Direction direction, TrackBits track) -{ - static const TrackBits state_dir_table[DIAGDIR_END] = { TRACK_BIT_RIGHT, TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER }; - - DiagDirection diagdir = DirToDiagDir(direction); - - /* Determine the diagonal direction in which we will exit this tile */ - if (!HasBit(direction, 0) && track != state_dir_table[diagdir]) { - diagdir = ChangeDiagDir(diagdir, DIAGDIRDIFF_90LEFT); - } - - return diagdir; -} - /** * Return the cargo weight multiplier to use for a rail vehicle @@ -1872,9 +1851,9 @@ void ReverseTrainDirection(Train *v) return; } - /* TrainExitDir does not always produce the desired dir for depots and + /* VehicleExitDir does not always produce the desired dir for depots and * tunnels/bridges that is needed for UpdateSignalsOnSegment. */ - DiagDirection dir = TrainExitDir(v->direction, v->track); + DiagDirection dir = VehicleExitDir(v->direction, v->track); if (IsRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)) dir = INVALID_DIAGDIR; if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) { @@ -3098,7 +3077,7 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data) /* not front engine of a train, inside wormhole or depot, crashed */ if (!t->IsFrontEngine() || !(t->track & TRACK_BIT_MASK)) return NULL; - if (t->cur_speed > 5 || TrainExitDir(t->direction, t->track) != exitdir) return NULL; + if (t->cur_speed > 5 || VehicleExitDir(t->direction, t->track) != exitdir) return NULL; return t; } @@ -3681,7 +3660,7 @@ static TileIndex TrainApproachingCrossingTile(const Train *v) if (!TrainCanLeaveTile(v)) return INVALID_TILE; - DiagDirection dir = TrainExitDir(v->direction, v->track); + DiagDirection dir = VehicleExitDir(v->direction, v->track); TileIndex tile = v->tile + TileOffsByDiagDir(dir); /* not a crossing || wrong axis || unusable rail (wrong type or owner) */ @@ -3718,7 +3697,7 @@ static bool TrainCheckIfLineEnds(Train *v, bool reverse) if (!TrainCanLeaveTile(v)) return true; /* Determine the non-diagonal direction in which we will exit this tile */ - DiagDirection dir = TrainExitDir(v->direction, v->track); + DiagDirection dir = VehicleExitDir(v->direction, v->track); /* Calculate next tile */ TileIndex tile = v->tile + TileOffsByDiagDir(dir); @@ -3786,7 +3765,7 @@ static bool TrainLocoHandler(Train *v, bool mode) /* Try to reserve a path when leaving the station as we * might not be marked as wanting a reservation, e.g. * when an overlength train gets turned around in a station. */ - DiagDirection dir = TrainExitDir(v->direction, v->track); + DiagDirection dir = VehicleExitDir(v->direction, v->track); if (IsRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)) dir = INVALID_DIAGDIR; if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) { diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index d1d4a4cd57..9559163c38 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -338,13 +338,13 @@ bool VideoDriver_Win32::MakeWindow(bool full_screen) if (_wnd.main_wnd != NULL) { if (!_window_maximize) SetWindowPos(_wnd.main_wnd, 0, 0, 0, w, h, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE); } else { - TCHAR Windowtitle[50]; int x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2; int y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2; - _sntprintf(Windowtitle, lengthof(Windowtitle), _T("OpenTTD %s"), MB_TO_WIDE(_openttd_revision)); + char window_title[64]; + seprintf(window_title, lastof(window_title), "OpenTTD %s", _openttd_revision); - _wnd.main_wnd = CreateWindow(_T("OTTD"), Windowtitle, style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0); + _wnd.main_wnd = CreateWindow(_T("OTTD"), MB_TO_WIDE(window_title), style, x, y, w, h, 0, 0, GetModuleHandle(NULL), 0); if (_wnd.main_wnd == NULL) usererror("CreateWindow failed"); ShowWindow(_wnd.main_wnd, showstyle); }