diff options
author | rubidium <rubidium@openttd.org> | 2009-08-31 22:38:37 +0000 |
---|---|---|
committer | rubidium <rubidium@openttd.org> | 2009-08-31 22:38:37 +0000 |
commit | bc8e66c202ce9af15aa12718939726eea42b25fd (patch) | |
tree | ecd7de355fd2c9defdde36a2e6f736836df9d7b4 /src/os | |
parent | 5cc6d1f894f224b3fdde6cdb5535ce4154172f09 (diff) | |
download | openttd-bc8e66c202ce9af15aa12718939726eea42b25fd.tar.xz |
(svn r17336) -Codechange: move some os specific files into src/os/
Diffstat (limited to 'src/os')
-rw-r--r-- | src/os/os2/os2.cpp | 243 | ||||
-rw-r--r-- | src/os/unix/unix.cpp | 314 | ||||
-rw-r--r-- | src/os/windows/masm64.rules | 266 | ||||
-rw-r--r-- | src/os/windows/ottdres.rc.in | 111 | ||||
-rw-r--r-- | src/os/windows/win32.cpp | 1378 | ||||
-rw-r--r-- | src/os/windows/win32.h | 55 | ||||
-rw-r--r-- | src/os/windows/win64.asm | 8 |
7 files changed, 2375 insertions, 0 deletions
diff --git a/src/os/os2/os2.cpp b/src/os/os2/os2.cpp new file mode 100644 index 000000000..06508b361 --- /dev/null +++ b/src/os/os2/os2.cpp @@ -0,0 +1,243 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file os2.cpp OS2 related OS support. */ + +#include "../../stdafx.h" +#include "../../openttd.h" +#include "../../variables.h" +#include "../../gui.h" +#include "../../fileio_func.h" +#include "../../fios.h" +#include "../../functions.h" +#include "../../core/random_func.hpp" +#include "../../string_func.h" +#include "../../textbuf_gui.h" + +#include "table/strings.h" + +#include <dirent.h> +#include <unistd.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <time.h> +#ifndef __INNOTEK_LIBC__ + #include <dos.h> +#endif + +#define INCL_WIN +#define INCL_WINCLIPBOARD + +#include <os2.h> +#ifndef __INNOTEK_LIBC__ + #include <i86.h> +#endif + +bool FiosIsRoot(const char *file) +{ + return file[3] == '\0'; +} + +void FiosGetDrives() +{ + uint disk, disk2, save, total; + +#ifndef __INNOTEK_LIBC__ + _dos_getdrive(&save); // save original drive +#else + save = _getdrive(); // save original drive + char wd[MAX_PATH]; + getcwd(wd, MAX_PATH); + total = 'z'; +#endif + + /* get an available drive letter */ +#ifndef __INNOTEK_LIBC__ + for (disk = 1;; disk++) { + _dos_setdrive(disk, &total); +#else + for (disk = 'A';; disk++) { + _chdrive(disk); +#endif + if (disk >= total) break; + +#ifndef __INNOTEK_LIBC__ + _dos_getdrive(&disk2); +#else + disk2 = _getdrive(); +#endif + + if (disk == disk2) { + FiosItem *fios = _fios_items.Append(); + fios->type = FIOS_TYPE_DRIVE; + fios->mtime = 0; +#ifndef __INNOTEK_LIBC__ + snprintf(fios->name, lengthof(fios->name), "%c:", 'A' + disk - 1); +#else + snprintf(fios->name, lengthof(fios->name), "%c:", disk); +#endif + strecpy(fios->title, fios->name, lastof(fios->title)); + } + } + + /* Restore the original drive */ +#ifndef __INNOTEK_LIBC__ + _dos_setdrive(save, &total); +#else + chdir(wd); +#endif +} + +bool FiosGetDiskFreeSpace(const char *path, uint64 *tot) +{ +#ifndef __INNOTEK_LIBC__ + struct diskfree_t free; + char drive = path[0] - 'A' + 1; + + if (tot != NULL && _getdiskfree(drive, &free) == 0) { + *tot = free.avail_clusters * free.sectors_per_cluster * free.bytes_per_sector; + return true; + } + + return false; +#else + uint64 free = 0; + +#ifdef HAS_STATVFS + { + struct statvfs s; + + if (statvfs(path, &s) != 0) return false; + free = (uint64)s.f_frsize * s.f_bavail; + } +#endif + if (tot != NULL) *tot = free; + return true; +#endif +} + +bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb) +{ + char filename[MAX_PATH]; + + snprintf(filename, lengthof(filename), "%s" PATHSEP "%s", path, ent->d_name); + return stat(filename, sb) == 0; +} + +bool FiosIsHiddenFile(const struct dirent *ent) +{ + return ent->d_name[0] == '.'; +} + +void ShowInfo(const char *str) +{ + HAB hab; + HMQ hmq; + ULONG rc; + + /* init PM env. */ + hmq = WinCreateMsgQueue((hab = WinInitialize(0)), 0); + + /* display the box */ + rc = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, (const unsigned char *)str, (const unsigned char *)"OpenTTD", 0, MB_OK | MB_MOVEABLE | MB_INFORMATION); + + /* terminate PM env. */ + WinDestroyMsgQueue(hmq); + WinTerminate(hab); +} + +void ShowOSErrorBox(const char *buf, bool system) +{ + HAB hab; + HMQ hmq; + ULONG rc; + + /* init PM env. */ + hmq = WinCreateMsgQueue((hab = WinInitialize(0)), 0); + + /* display the box */ + rc = WinMessageBox(HWND_DESKTOP, HWND_DESKTOP, (const unsigned char *)buf, (const unsigned char *)"OpenTTD", 0, MB_OK | MB_MOVEABLE | MB_ERROR); + + /* terminate PM env. */ + WinDestroyMsgQueue(hmq); + WinTerminate(hab); +} + +int CDECL main(int argc, char *argv[]) +{ + SetRandomSeed(time(NULL)); + + return ttd_main(argc, argv); +} + +/** + * Insert a chunk of text from the clipboard onto the textbuffer. Get TEXT clipboard + * and append this up to the maximum length (either absolute or screenlength). If maxlength + * is zero, we don't care about the screenlength but only about the physical length of the string + * @param tb Textbuf type to be changed + * @return Return true on successful change of Textbuf, or false otherwise + */ +bool InsertTextBufferClipboard(Textbuf *tb) +{ +/* XXX -- Currently no clipboard support implemented with GCC */ +#ifndef __INNOTEK_LIBC__ + HAB hab = 0; + + if (WinOpenClipbrd(hab)) + { + const char *text = (const char*)WinQueryClipbrdData(hab, CF_TEXT); + + if (text != NULL) + { + uint length = 0; + uint width = 0; + const char *i; + + for (i = text; IsValidAsciiChar(*i); i++) + { + uint w; + + if (tb->size + length + 1 > tb->maxsize) break; + + w = GetCharacterWidth(FS_NORMAL, (byte)*i); + if (tb->maxwidth != 0 && width + tb->width + w > tb->maxwidth) break; + + width += w; + length++; + } + + memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->size - tb->caretpos); + memcpy(tb->buf + tb->caretpos, text, length); + tb->width += width; + tb->caretxoffs += width; + tb->size += length; + tb->caretpos += length; + + WinCloseClipbrd(hab); + return true; + } + + WinCloseClipbrd(hab); + } +#endif + return false; +} + + +void CSleep(int milliseconds) +{ +#ifndef __INNOTEK_LIBC__ + delay(milliseconds); +#else + usleep(milliseconds * 1000); +#endif +} + +const char *FS2OTTD(const char *name) {return name;} +const char *OTTD2FS(const char *name) {return name;} diff --git a/src/os/unix/unix.cpp b/src/os/unix/unix.cpp new file mode 100644 index 000000000..bbe916c57 --- /dev/null +++ b/src/os/unix/unix.cpp @@ -0,0 +1,314 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file unix.cpp Implementation of Unix specific file handling. */ + +#include "../../stdafx.h" +#include "../../openttd.h" +#include "../../variables.h" +#include "../../textbuf_gui.h" +#include "../../functions.h" +#include "../../core/random_func.hpp" + +#include "table/strings.h" + +#include <dirent.h> +#include <unistd.h> +#include <sys/stat.h> +#include <time.h> +#include <signal.h> + +#if (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L) || defined(__GLIBC__) + #define HAS_STATVFS +#endif + +#ifdef HAS_STATVFS +#include <sys/statvfs.h> +#endif + + +#ifdef __MORPHOS__ +#include <exec/types.h> +ULONG __stack = (1024*1024)*2; // maybe not that much is needed actually ;) + +/* The system supplied definition of SIG_IGN does not match */ +#undef SIG_IGN +#define SIG_IGN (void (*)(int))1 +#endif /* __MORPHOS__ */ + +#ifdef __AMIGA__ +#warning add stack symbol to avoid that user needs to set stack manually (tokai) +// ULONG __stack = +#endif + +#if defined(__APPLE__) + #if defined(WITH_SDL) + /* the mac implementation needs this file included in the same file as main() */ + #include <SDL.h> + #endif +#endif + +bool FiosIsRoot(const char *path) +{ +#if !defined(__MORPHOS__) && !defined(__AMIGAOS__) + return path[1] == '\0'; +#else + /* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */ + const char *s = strchr(path, ':'); + return s != NULL && s[1] == '\0'; +#endif +} + +void FiosGetDrives() +{ + return; +} + +bool FiosGetDiskFreeSpace(const char *path, uint64 *tot) +{ + uint64 free = 0; + +#ifdef HAS_STATVFS +# ifdef __APPLE__ + /* OSX 10.3 lacks statvfs so don't try to use it even though later versions of OSX has it. */ + if (MacOSVersionIsAtLeast(10, 4, 0)) +# endif + { + struct statvfs s; + + if (statvfs(path, &s) != 0) return false; + free = (uint64)s.f_frsize * s.f_bavail; + } +#endif + if (tot != NULL) *tot = free; + return true; +} + +bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb) +{ + char filename[MAX_PATH]; + +#if defined(__MORPHOS__) || defined(__AMIGAOS__) + /* On MorphOS or AmigaOS paths look like: "Volume:directory/subdirectory" */ + if (FiosIsRoot(path)) { + snprintf(filename, lengthof(filename), "%s:%s", path, ent->d_name); + } else // XXX - only next line! +#else + assert(path[strlen(path) - 1] == PATHSEPCHAR); + if (strlen(path) > 2) assert(path[strlen(path) - 2] != PATHSEPCHAR); +#endif + snprintf(filename, lengthof(filename), "%s%s", path, ent->d_name); + + return stat(filename, sb) == 0; +} + +bool FiosIsHiddenFile(const struct dirent *ent) +{ + return ent->d_name[0] == '.'; +} + +#ifdef WITH_ICONV + +#include <iconv.h> +#include <errno.h> +#include "../../debug.h" +#include "../../string_func.h" + +const char *GetCurrentLocale(const char *param); + +#define INTERNALCODE "UTF-8" + +/** Try and try to decipher the current locale from environmental + * variables. MacOSX is hardcoded, other OS's are dynamic. If no suitable + * locale can be found, don't do any conversion "" */ +static const char *GetLocalCode() +{ +#if defined(__APPLE__) + return "UTF-8-MAC"; +#else + /* Strip locale (eg en_US.UTF-8) to only have UTF-8 */ + const char *locale = GetCurrentLocale("LC_CTYPE"); + if (locale != NULL) locale = strchr(locale, '.'); + + return (locale == NULL) ? "" : locale + 1; +#endif +} + +/** FYI: This is not thread-safe. + * convert between locales, which from and which to is set in the calling + * functions OTTD2FS() and FS2OTTD(). You should NOT use this function directly + * NOTE: iconv was added in OSX 10.3. 10.2.x will still have the invalid char + * issues. There aren't any easy fix for this */ +static const char *convert_tofrom_fs(iconv_t convd, const char *name) +{ + static char buf[1024]; + /* Work around buggy iconv implementation where inbuf is wrongly typed as + * non-const. Correct implementation is at + * http://www.opengroup.org/onlinepubs/007908799/xsh/iconv.html */ +#ifdef HAVE_BROKEN_ICONV + char *inbuf = (char*)name; +#else + const char *inbuf = name; +#endif + + char *outbuf = buf; + size_t outlen = sizeof(buf) - 1; + size_t inlen = strlen(name); + + strecpy(outbuf, name, outbuf + outlen); + + iconv(convd, NULL, NULL, NULL, NULL); + if (iconv(convd, &inbuf, &inlen, &outbuf, &outlen) == (size_t)(-1)) { + DEBUG(misc, 0, "[iconv] error converting '%s'. Errno %d", name, errno); + } + + *outbuf = '\0'; + /* FIX: invalid characters will abort conversion, but they shouldn't occur? */ + return buf; +} + +/** Convert from OpenTTD's encoding to that of the local environment + * @param name pointer to a valid string that will be converted + * @return pointer to a new stringbuffer that contains the converted string */ +const char *OTTD2FS(const char *name) +{ + static iconv_t convd = (iconv_t)(-1); + + if (convd == (iconv_t)(-1)) { + const char *env = GetLocalCode(); + convd = iconv_open(env, INTERNALCODE); + if (convd == (iconv_t)(-1)) { + DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", INTERNALCODE, env); + return name; + } + } + + return convert_tofrom_fs(convd, name); +} + +/** Convert to OpenTTD's encoding from that of the local environment + * @param name pointer to a valid string that will be converted + * @return pointer to a new stringbuffer that contains the converted string */ +const char *FS2OTTD(const char *name) +{ + static iconv_t convd = (iconv_t)(-1); + + if (convd == (iconv_t)(-1)) { + const char *env = GetLocalCode(); + convd = iconv_open(INTERNALCODE, env); + if (convd == (iconv_t)(-1)) { + DEBUG(misc, 0, "[iconv] conversion from codeset '%s' to '%s' unsupported", env, INTERNALCODE); + return name; + } + } + + return convert_tofrom_fs(convd, name); +} + +#else +const char *FS2OTTD(const char *name) {return name;} +const char *OTTD2FS(const char *name) {return name;} +#endif /* WITH_ICONV */ + +void ShowInfo(const char *str) +{ + fprintf(stderr, "%s\n", str); +} + +void ShowOSErrorBox(const char *buf, bool system) +{ +#if defined(__APPLE__) + /* this creates an NSAlertPanel with the contents of 'buf' + * this is the native and nicest way to do this on OSX */ + ShowMacDialog( buf, "See readme for more info\nMost likely you are missing files from the original TTD", "Quit" ); +#else + /* All unix systems, except OSX. Only use escape codes on a TTY. */ + if (isatty(fileno(stderr))) { + fprintf(stderr, "\033[1;31mError: %s\033[0;39m\n", buf); + } else { + fprintf(stderr, "Error: %s\n", buf); + } +#endif +} + +#ifdef WITH_COCOA +void cocoaSetupAutoreleasePool(); +void cocoaReleaseAutoreleasePool(); +#endif + +int CDECL main(int argc, char *argv[]) +{ + int ret; + +#ifdef WITH_COCOA + cocoaSetupAutoreleasePool(); + /* This is passed if we are launched by double-clicking */ + if (argc >= 2 && strncmp(argv[1], "-psn", 4) == 0) { + argv[1] = NULL; + argc = 1; + } +#endif + + SetRandomSeed(time(NULL)); + + signal(SIGPIPE, SIG_IGN); + + ret = ttd_main(argc, argv); + +#ifdef WITH_COCOA + cocoaReleaseAutoreleasePool(); +#endif + + return ret; +} + +bool InsertTextBufferClipboard(Textbuf *tb) +{ + return false; +} + + +/* multi os compatible sleep function */ + +#ifdef __AMIGA__ +/* usleep() implementation */ +# include <devices/timer.h> +# include <dos/dos.h> + + extern struct Device *TimerBase = NULL; + extern struct MsgPort *TimerPort = NULL; + extern struct timerequest *TimerRequest = NULL; +#endif /* __AMIGA__ */ + +void CSleep(int milliseconds) +{ + #if defined(PSP) + sceKernelDelayThread(milliseconds * 1000); + #elif defined(__BEOS__) + snooze(milliseconds * 1000); + #elif defined(__AMIGA__) + { + ULONG signals; + ULONG TimerSigBit = 1 << TimerPort->mp_SigBit; + + /* send IORequest */ + TimerRequest->tr_node.io_Command = TR_ADDREQUEST; + TimerRequest->tr_time.tv_secs = (milliseconds * 1000) / 1000000; + TimerRequest->tr_time.tv_micro = (milliseconds * 1000) % 1000000; + SendIO((struct IORequest *)TimerRequest); + + if (!((signals = Wait(TimerSigBit | SIGBREAKF_CTRL_C)) & TimerSigBit) ) { + AbortIO((struct IORequest *)TimerRequest); + } + WaitIO((struct IORequest *)TimerRequest); + } + #else + usleep(milliseconds * 1000); + #endif +} diff --git a/src/os/windows/masm64.rules b/src/os/windows/masm64.rules new file mode 100644 index 000000000..62e6e0f8b --- /dev/null +++ b/src/os/windows/masm64.rules @@ -0,0 +1,266 @@ +<?xml version="1.0" encoding="utf-8"?> +<VisualStudioToolFile + Name="Microsoft Macro Assembler 64" + Version="8.00" + > + <Rules> + <CustomBuildRule + Name="MASM AMD64" + DisplayName="Microsoft Macro Assembler for AMD64" + CommandLine="ml64.exe /c [AllOptions] [AdditionalOptions] /Ta[inputs]" + Outputs="[$ObjectFileName]" + FileExtensions="*.asm" + ExecutionDescription="Assembling..." + > + <Properties> + <BooleanProperty + Name="NoLogo" + DisplayName="Suppress Startup Banner" + Description="Suppress the display of the startup banner and information messages. (/nologo)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/nologo" + DefaultValue="true" + /> + <StringProperty + Name="ObjectFileName" + DisplayName="Object File Name" + PropertyPageName="Object File" + Description="Specifies the name of the output object file. (/Fo:[file])" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Fo"[value]"" + DefaultValue="$(IntDir)\$(InputName).obj" + /> + <BooleanProperty + Name="PreserveIdentifierCase" + DisplayName="Preserve Identifier Case" + PropertyPageName="Identifiers" + Description="Preserves case of all user identifiers. (/Cp)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Cp" + /> + <BooleanProperty + Name="PreservePublicAndExternSymbolCase" + DisplayName="Preserve Public and Extern Symbol Case" + PropertyPageName="Identifiers" + Description="Preserves case in public and extern symbols. (/Cx)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Cx" + /> + <StringProperty + Name="PreprocessorDefinitions" + DisplayName="Preprocessor Definitions" + Description="Defines a text macro with the given name. (/D[symbol])" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/D"[value]"" + Delimited="true" + Inheritable="true" + /> + <BooleanProperty + Name="GeneratePreprocessedSourceListing" + DisplayName="Generate Preprocessed Source Listing" + PropertyPageName="Listing File" + Description="Generates a preprocessed source listing to the Output Window. (/EP)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/EP" + /> + <StringProperty + Name="AssembledCodeListingFile" + DisplayName="Assembled Code Listing File" + PropertyPageName="Listing File" + Description="Generates an assembled code listing file. (/Fl[file])" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Fl"[value]"" + /> + <StringProperty + Name="SourceBrowserFile" + DisplayName="Source Browser File" + PropertyPageName="Source Browser File" + Description="Generates a source browser .sbr file. (/Fr[file])" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Fr"[value]"" + /> + <StringProperty + Name="ExtendedSourceBrowserFile" + DisplayName="Extended Source Browser File" + PropertyPageName="Source Browser File" + Description="Generates an extended form of a source browser .sbr file. (/FR[file])" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/FR"[value]"" + /> + <StringProperty + Name="IncludePaths" + DisplayName="Include Paths" + Description="Sets path for include file. A maximum of 10 /I options is allowed. (/I [path])" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/I "[value]"" + Delimited="true" + Inheritable="true" + /> + <BooleanProperty + Name="ListAllAvailableInformation" + DisplayName="List All Available Information" + PropertyPageName="Listing File" + Description="Turns on listing of all available information. (/Sa)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Sa" + /> + <BooleanProperty + Name="AddInstructionTimings" + DisplayName="Add Instruction Timings" + PropertyPageName="Listing File" + Description="Adds instruction timings to listing file. (/Sc)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Sc" + /> + <BooleanProperty + Name="AddFirstPassListing" + DisplayName="Add First Pass Listing" + PropertyPageName="Listing File" + Description="Adds first-pass listing to listing file. (/Sf)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Sf" + /> + <IntegerProperty + Name="SourceListingLineWidth" + DisplayName="Source Listing Line Width" + PropertyPageName="Listing File" + Description="Sets the line width of source listing in characters per line. Range is 60 to 255 or 0. Default is 0. Same as PAGE width. (/Sl [width])" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Sl [value]" + /> + <BooleanProperty + Name="DisableSymbolTable" + DisplayName="Disable Symbol Table" + PropertyPageName="Listing File" + Description="Turns off symbol table when producing a listing. (/Sn)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Sn" + /> + <IntegerProperty + Name="SourceListingPageLength" + DisplayName="Source Listing Page Length" + PropertyPageName="Listing File" + Description="Sets the page length of source listing in lines per page. Range is 10 to 255 or 0. Default is 0. Same as PAGE length. (/Sp [length])" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Sp [value]" + /> + <StringProperty + Name="SourceListingSubTitle" + DisplayName="Source Listing Subtitle" + PropertyPageName="Listing File" + Description="Specifies subtitle text for source listing. Same as SUBTITLE text. (/Ss [subtitle])" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Ss [value]" + /> + <StringProperty + Name="SourceListingTitle" + DisplayName="Source Listing Title" + PropertyPageName="Listing File" + Description="Specifies title for source listing. Same as TITLE text. (/St [title])" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/St [value]" + /> + <BooleanProperty + Name="EnableFalseConditionalsInListing" + DisplayName="Enable False Conditionals In Listing" + PropertyPageName="Listing File" + Description="Turns on false conditionals in listing. (/Sx)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Sx" + /> + <EnumProperty + Name="WarningLevel" + DisplayName="Warning Level" + Description="Sets the warning level, where level = 0, 1, 2, or 3. (/W0, /W1, /W2, /W3)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + > + <Values> + <EnumValue + Value="0" + Switch="/W0" + DisplayName="Warning Level 0 (/W0)" + /> + <EnumValue + Value="1" + Switch="/W1" + DisplayName="Warning Level 1 (/W1)" + /> + <EnumValue + Value="2" + Switch="/W2" + DisplayName="Warning Level 2 (/W2)" + /> + <EnumValue + Value="3" + Switch="/W3" + DisplayName="Warning Level 3 (/W3)" + /> + </Values> + </EnumProperty> + <BooleanProperty + Name="TreatWarningsAsErrors" + DisplayName="Treat Warnings As Errors" + Description="Returns an error code if warnings are generated. (/WX)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/WX" + /> + <BooleanProperty + Name="GenerateLineInformation" + DisplayName="Generate Line Information" + PropertyPageName="Object File" + Description="Generates line-number information in object file. (/Zd)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Zd" + /> + <BooleanProperty + Name="MakeAllSymbolsPublic" + DisplayName="Make All Symbols Public" + PropertyPageName="Object File" + Description="Makes all symbols public. (/Zf)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Zf" + /> + <BooleanProperty + Name="GenerateCodeViewInformation" + DisplayName="Generate CodeView Information" + PropertyPageName="Object File" + Description="Generates CodeView information in object file. (/Zi)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Zi" + /> + <EnumProperty + Name="PackAlignmentBoundary" + DisplayName="Pack Alignment Boundary" + PropertyPageName="Advanced" + Description="Packs structures on the specified byte boundary. The alignment can be 1, 2, or 4. (/Zp1, /Zp2, /Zp4)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + > + <Values> + <EnumValue + Value="0" + Switch="/Zp1" + DisplayName="One Byte Boundary (/Zp1)" + /> + <EnumValue + Value="1" + Switch="/Zp2" + DisplayName="Two Byte Boundary (/Zp2)" + /> + <EnumValue + Value="2" + Switch="/Zp4" + DisplayName="Four Byte Boundary (/Zp4)" + /> + </Values> + </EnumProperty> + <BooleanProperty + Name="PerformSyntaxCheckOnly" + DisplayName="Perform Syntax Check Only" + Description="Performs a syntax check only. (/Zs)" + HelpURL="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmasm/html/vclrfml.asp" + Switch="/Zs" + /> + </Properties> + </CustomBuildRule> + </Rules> +</VisualStudioToolFile> diff --git a/src/os/windows/ottdres.rc.in b/src/os/windows/ottdres.rc.in new file mode 100644 index 000000000..f0989f5bb --- /dev/null +++ b/src/os/windows/ottdres.rc.in @@ -0,0 +1,111 @@ +//Microsoft Developer Studio generated resource script. +// $Id$ +// +// This file is part of OpenTTD. +// OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. +// OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. +// +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_READONLY_SYMBOLS +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS +#ifdef MSVC +#include "winres.h" +#else +#define IDC_STATIC (-1) // all static controls +#endif + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Neutral (Default) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUD) +#ifdef _WIN32 +LANGUAGE LANG_NEUTRAL, SUBLANG_DEFAULT +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +100 ICON DISCARDABLE "../media/openttd.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +100 DIALOG DISCARDABLE 0, 0, 305, 77 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Fatal Application Failure" +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "&Close",12,7,58,50,14 +// PUSHBUTTON "&Submit report",14,81,58,68,14,WS_DISABLED + PUSHBUTTON "&Emergency save",13,155,58,68,14 + PUSHBUTTON "",15,243,58,55,14 + EDITTEXT 11,7,79,291,118,ES_MULTILINE | ES_READONLY | WS_VSCROLL | + WS_HSCROLL | NOT WS_TABSTOP + LTEXT "",10,36,7,262,43 + ICON 100,IDC_STATIC,9,9,20,20 +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,8,0,!!REVISION!! + PRODUCTVERSION 0,8,0,!!REVISION!! + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "Comments", "This program is licensed under the GNU General Public License.\0" + VALUE "CompanyName", "OpenTTD Development Team\0" + VALUE "FileDescription", "OpenTTD\0" + VALUE "FileVersion", "Development !!VERSION!!\0" + VALUE "InternalName", "openttd\0" + VALUE "LegalCopyright", "Copyright \xA9 OpenTTD Developers 2002-2009. All Rights Reserved.\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "openttd.exe\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "OpenTTD\0" + VALUE "ProductVersion", "Development !!VERSION!!\0" + VALUE "SpecialBuild", "-\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + +#endif // !_MAC + +#endif // Neutral (Default) resources +///////////////////////////////////////////////////////////////////////////// diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp new file mode 100644 index 000000000..432755fbe --- /dev/null +++ b/src/os/windows/win32.cpp @@ -0,0 +1,1378 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file win32.cpp Implementation of MS Windows system calls */ + +#include "../../stdafx.h" +#include "../../openttd.h" +#include "../../debug.h" +#include "../../saveload/saveload.h" +#include "../../gfx_func.h" +#include "../../textbuf_gui.h" +#include "../../fileio_func.h" +#include "../../fios.h" +#include "../../rev.h" +#include <windows.h> +#include <winnt.h> +#include <wininet.h> +#include <fcntl.h> +#include <shlobj.h> /* SHGetFolderPath */ +#include "../../variables.h" +#include "win32.h" +#include "../../core/alloc_func.hpp" +#include "../../functions.h" +#include "../../core/random_func.hpp" +#include "../../core/bitmath_func.hpp" +#include "../../string_func.h" +#include "../../gamelog.h" +#include <ctype.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#if defined(_MSC_VER) && !defined(WINCE) + #include <dbghelp.h> + #include "../../strings_func.h" +#endif + +static bool _has_console; + +static bool cursor_visible = true; + +bool MyShowCursor(bool show) +{ + if (cursor_visible == show) return show; + + cursor_visible = show; + ShowCursor(show); + + return !show; +} + +/** Helper function needed by dynamically loading libraries + * XXX: Hurray for MS only having an ANSI GetProcAddress function + * on normal windows and no Wide version except for in Windows Mobile/CE */ +bool LoadLibraryList(Function proc[], const char *dll) +{ + while (*dll != '\0') { + HMODULE lib; + lib = LoadLibrary(MB_TO_WIDE(dll)); + + if (lib == NULL) return false; + for (;;) { + FARPROC p; + + while (*dll++ != '\0') { /* Nothing */ } + if (*dll == '\0') break; +#if defined(WINCE) + p = GetProcAddress(lib, MB_TO_WIDE(dll)); +#else + p = GetProcAddress(lib, dll); +#endif + if (p == NULL) return false; + *proc++ = (Function)p; + } + dll++; + } + return true; +} + +#ifdef _MSC_VER +static const char *_exception_string = NULL; +void SetExceptionString(const char *s, ...) +{ + va_list va; + char buf[512]; + + va_start(va, s); + vsnprintf(buf, lengthof(buf), s, va); + va_end(va); + + _exception_string = strdup(buf); +} +#endif + +void ShowOSErrorBox(const char *buf, bool system) +{ + MyShowCursor(true); + MessageBox(GetActiveWindow(), MB_TO_WIDE(buf), _T("Error!"), MB_ICONSTOP); + +/* if exception tracker is enabled, we crash here to let the exception handler handle it. */ +#if defined(WIN32_EXCEPTION_TRACKER) && !defined(_DEBUG) + if (system) { + _exception_string = buf; + *(byte*)0 = 0; + } +#endif +} + +#if defined(_MSC_VER) && !defined(WINCE) + +static void *_safe_esp; +static char *_crash_msg; +static bool _expanded; +static bool _did_emerg_save; +static int _ident; + +struct DebugFileInfo { + uint32 size; + uint32 crc32; + SYSTEMTIME file_time; +}; + +static uint32 *_crc_table; + +static void MakeCRCTable(uint32 *table) +{ + uint32 crc, poly = 0xEDB88320L; + int i; + int j; + + _crc_table = table; + + for (i = 0; i != 256; i++) { + crc = i; + for (j = 8; j != 0; j--) { + crc = (crc & 1 ? (crc >> 1) ^ poly : crc >> 1); + } + table[i] = crc; + } +} + +static uint32 CalcCRC(byte *data, uint size, uint32 crc) +{ + for (; size > 0; size--) { + crc = ((crc >> 8) & 0x00FFFFFF) ^ _crc_table[(crc ^ *data++) & 0xFF]; + } + return crc; +} + +static void GetFileInfo(DebugFileInfo *dfi, const TCHAR *filename) +{ + HANDLE file; + memset(dfi, 0, sizeof(*dfi)); + + file = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); + if (file != INVALID_HANDLE_VALUE) { + byte buffer[1024]; + DWORD numread; + uint32 filesize = 0; + FILETIME write_time; + uint32 crc = (uint32)-1; + + for (;;) { + if (ReadFile(file, buffer, sizeof(buffer), &numread, NULL) == 0 || numread == 0) + break; + filesize += numread; + crc = CalcCRC(buffer, numread, crc); + } + dfi->size = filesize; + dfi->crc32 = crc ^ (uint32)-1; + + if (GetFileTime(file, NULL, NULL, &write_time)) { + FileTimeToSystemTime(&write_time, &dfi->file_time); + } + CloseHandle(file); + } +} + + +static char *PrintModuleInfo(char *output, const char *last, HMODULE mod) +{ + TCHAR buffer[MAX_PATH]; + DebugFileInfo dfi; + + GetModuleFileName(mod, buffer, MAX_PATH); + GetFileInfo(&dfi, buffer); + output += seprintf(output, last, " %-20s handle: %p size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\r\n", + WIDE_TO_MB(buffer), + mod, + dfi.size, + dfi.crc32, + dfi.file_time.wYear, + dfi.file_time.wMonth, + dfi.file_time.wDay, + dfi.file_time.wHour, + dfi.file_time.wMinute, + dfi.file_time.wSecond + ); + return output; +} + +static char *PrintModuleList(char *output, const char *last) +{ + BOOL (WINAPI *EnumProcessModules)(HANDLE, HMODULE*, DWORD, LPDWORD); + + if (LoadLibraryList((Function*)&EnumProcessModules, "psapi.dll\0EnumProcessModules\0\0")) { + HMODULE modules[100]; + DWORD needed; + BOOL res; + + HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()); + if (proc != NULL) { + res = EnumProcessModules(proc, modules, sizeof(modules), &needed); + CloseHandle(proc); + if (res) { + size_t count = min(needed / sizeof(HMODULE), lengthof(modules)); + + for (size_t i = 0; i != count; i++) output = PrintModuleInfo(output, last, modules[i]); + return output; + } + } + } + output = PrintModuleInfo(output, last, NULL); + return output; +} + +static const TCHAR _crash_desc[] = + _T("A serious fault condition occured in the game. The game will shut down.\n") + _T("Please send the crash information and the crash.dmp file (if any) to the developers.\n") + _T("This will greatly help debugging. The correct place to do this is http://bugs.openttd.org. ") + _T("The information contained in the report is displayed below.\n") + _T("Press \"Emergency save\" to attempt saving the game."); + +static const TCHAR _save_succeeded[] = + _T("Emergency save succeeded.\n") + _T("Be aware that critical parts of the internal game state may have become ") + _T("corrupted. The saved game is not guaranteed to work."); + +static const TCHAR _emergency_crash[] = + _T("A serious fault condition occured in the game. The game will shut down.\n") + _T("As you loaded an emergency savegame no crash information will be generated.\n"); + +static bool EmergencySave() +{ + GamelogStartAction(GLAT_EMERGENCY); + GamelogEmergency(); + GamelogStopAction(); + SaveOrLoad("crash.sav", SL_SAVE, BASE_DIR); + return true; +} + +/* Disable the crash-save submit code as it's not used */ +#if 0 + +struct WinInetProcs { + HINTERNET (WINAPI *InternetOpen)(LPCTSTR, DWORD, LPCTSTR, LPCTSTR, DWORD); + HINTERNET (WINAPI *InternetConnect)(HINTERNET, LPCTSTR, INTERNET_PORT, LPCTSTR, LPCTSTR, DWORD, DWORD, DWORD); + HINTERNET (WINAPI *HttpOpenRequest)(HINTERNET, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR *, DWORD, DWORD); + BOOL (WINAPI *HttpSendRequest)(HINTERNET, LPCTSTR, DWORD, LPVOID, DWORD); + BOOL (WINAPI *InternetCloseHandle)(HINTERNET); + BOOL (WINAPI *HttpQueryInfo)(HINTERNET, DWORD, LPVOID, LPDWORD, LPDWORD); +}; + +#define M(x) x "\0" +#if defined(UNICODE) +# define W(x) x "W" +#else +# define W(x) x "A" +#endif +static const char wininet_files[] = + M("wininet.dll") + M(W("InternetOpen")) + M(W("InternetConnect")) + M(W("HttpOpenRequest")) + M(W("HttpSendRequest")) + M("InternetCloseHandle") + M(W("HttpQueryInfo")) + M(""); +#undef W +#undef M + +static WinInetProcs _wininet; + +static const TCHAR *SubmitCrashReport(HWND wnd, void *msg, size_t msglen, const TCHAR *arg) +{ + HINTERNET inet, conn, http; + const TCHAR *err = NULL; + DWORD code, len; + static TCHAR buf[100]; + TCHAR buff[100]; + + if (_wininet.InternetOpen == NULL && !LoadLibraryList((Function*)&_wininet, wininet_files)) return _T("can't load wininet.dll"); + + inet = _wininet.InternetOpen(_T("OTTD"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 ); + if (inet == NULL) { err = _T("internetopen failed"); goto error1; } + + conn = _wininet.InternetConnect(inet, _T("www.openttd.org"), INTERNET_DEFAULT_HTTP_PORT, _T(""), _T(""), INTERNET_SERVICE_HTTP, 0, 0); + if (conn == NULL) { err = _T("internetconnect failed"); goto error2; } + + _sntprintf(buff, lengthof(buff), _T("/crash.php?file=%s&ident=%d"), arg, _ident); + + http = _wininet.HttpOpenRequest(conn, _T("POST"), buff, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE , 0); + if (http == NULL) { err = _T("httpopenrequest failed"); goto error3; } + + if (!_wininet.HttpSendRequest(http, _T("Content-type: application/binary"), -1, msg, (DWORD)msglen)) { err = _T("httpsendrequest failed"); goto error4; } + + len = sizeof(code); + if (!_wininet.HttpQueryInfo(http, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &code, &len, 0)) { err = _T("httpqueryinfo failed"); goto error4; } + + if (code != 200) { + int l = _sntprintf(buf, lengthof(buf), _T("Server said: %d "), code); + len = sizeof(buf) - l; + _wininet.HttpQueryInfo(http, HTTP_QUERY_STATUS_TEXT, buf + l, &len, 0); + err = buf; + } + +error4: + _wininet.InternetCloseHandle(http); +error3: + _wininet.InternetCloseHandle(conn); +error2: + _wininet.InternetCloseHandle(inet); +error1: + return err; +} + +static void SubmitFile(HWND wnd, const TCHAR *file) +{ + HANDLE h; + unsigned long size; + unsigned long read; + void *mem; + + h = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (h == NULL) return; + + size = GetFileSize(h, NULL); + if (size > 500000) goto error1; + + mem = MallocT<byte>(size); + if (mem == NULL) goto error1; + + if (!ReadFile(h, mem, size, &read, NULL) || read != size) goto error2; + + SubmitCrashReport(wnd, mem, size, file); + +error2: + free(mem); +error1: + CloseHandle(h); +} + +#endif /* Disabled crash-submit procedures */ + +static const TCHAR * const _expand_texts[] = {_T("S&how report >>"), _T("&Hide report <<") }; + +static void SetWndSize(HWND wnd, int mode) +{ + RECT r, r2; + int offs; + + GetWindowRect(wnd, &r); + + SetDlgItemText(wnd, 15, _expand_texts[mode == 1]); + + if (mode >= 0) { + GetWindowRect(GetDlgItem(wnd, 11), &r2); + offs = r2.bottom - r2.top + 10; + if (!mode) offs = -offs; + SetWindowPos(wnd, HWND_TOPMOST, 0, 0, + r.right - r.left, r.bottom - r.top + offs, SWP_NOMOVE | SWP_NOZORDER); + } else { + SetWindowPos(wnd, HWND_TOPMOST, + (GetSystemMetrics(SM_CXSCREEN) - (r.right - r.left)) / 2, + (GetSystemMetrics(SM_CYSCREEN) - (r.bottom - r.top)) / 2, + 0, 0, SWP_NOSIZE); + } +} + +static bool DoEmergencySave(HWND wnd) +{ + bool b = false; + + EnableWindow(GetDlgItem(wnd, 13), FALSE); + _did_emerg_save = true; + __try { + b = EmergencySave(); + } __except (1) {} + return b; +} + +static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: { +#if defined(UNICODE) + /* We need to put the crash-log in a seperate buffer because the default + * buffer in MB_TO_WIDE is not large enough (512 chars) */ + wchar_t crash_msgW[8096]; +#endif + SetDlgItemText(wnd, 10, _crash_desc); + SetDlgItemText(wnd, 11, MB_TO_WIDE_BUFFER(_crash_msg, crash_msgW, lengthof(crash_msgW))); + SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE); + SetWndSize(wnd, -1); + } return TRUE; + case WM_COMMAND: + switch (wParam) { + case 12: // Close + ExitProcess(0); + case 13: // Emergency save + if (DoEmergencySave(wnd)) { + MessageBox(wnd, _save_succeeded, _T("Save successful"), MB_ICONINFORMATION); + } else { + MessageBox(wnd, _T("Save failed"), _T("Save failed"), MB_ICONINFORMATION); + } + break; +/* Disable the crash-save submit code as it's not used */ +#if 0 + case 14: { // Submit crash report + const TCHAR *s; + + SetCursor(LoadCursor(NULL, IDC_WAIT)); + + s = SubmitCrashReport(wnd, _crash_msg, strlen(_crash_msg), _T("")); + if (s != NULL) { + MessageBox(wnd, s, _T("Error"), MB_ICONSTOP); + break; + } + + /* try to submit emergency savegame */ + if (_did_emerg_save || DoEmergencySave(wnd)) SubmitFile(wnd, _T("crash.sav")); + + /* try to submit the autosaved game */ + if (_opt.autosave) { + TCHAR buf[40]; + _sntprintf(buf, lengthof(buf), _T("autosave%d.sav"), (_autosave_ctr - 1) & 3); + SubmitFile(wnd, buf); + } + EnableWindow(GetDlgItem(wnd, 14), FALSE); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + MessageBox(wnd, _T("Crash report submitted. Thank you."), _T("Crash Report"), MB_ICONINFORMATION); + } break; +#endif /* Disabled crash-submit procedures */ + case 15: // Expand window to show crash-message + _expanded ^= 1; + SetWndSize(wnd, _expanded); + break; + } + return TRUE; + case WM_CLOSE: ExitProcess(0); + } + + return FALSE; +} + +static void Handler2() +{ + ShowCursor(TRUE); + ShowWindow(GetActiveWindow(), FALSE); + DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(100), NULL, CrashDialogFunc); +} + +extern bool CloseConsoleLogIfActive(); + +static HANDLE _file_crash_log; + +static void GamelogPrintCrashLogProc(const char *s) +{ + DWORD num_written; + WriteFile(_file_crash_log, s, (DWORD)strlen(s), &num_written, NULL); + WriteFile(_file_crash_log, "\r\n", (DWORD)strlen("\r\n"), &num_written, NULL); +} + +/** Amount of output for the execption handler. */ +static const int EXCEPTION_OUTPUT_SIZE = 8192; + +static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep) +{ + char *output; + static bool had_exception = false; + + if (had_exception) ExitProcess(0); + if (GamelogTestEmergency()) { + MessageBox(NULL, _emergency_crash, _T("Fatal Application Failure"), MB_ICONERROR); + ExitProcess(0); + } + had_exception = true; + + _ident = GetTickCount(); // something pretty unique + + MakeCRCTable(AllocaM(uint32, 256)); + _crash_msg = output = (char*)LocalAlloc(LMEM_FIXED, EXCEPTION_OUTPUT_SIZE); + const char *last = output + EXCEPTION_OUTPUT_SIZE - 1; + + { + SYSTEMTIME time; + GetLocalTime(&time); + output += seprintf(output, last, + "*** OpenTTD Crash Report ***\r\n" + "Date: %d-%.2d-%.2d %.2d:%.2d:%.2d\r\n" + "Build: %s (%d) built on " __DATE__ " " __TIME__ "\r\n", + time.wYear, + time.wMonth, + time.wDay, + time.wHour, + time.wMinute, + time.wSecond, + _openttd_revision, + _openttd_revision_modified + ); + } + + if (_exception_string) + output += seprintf(output, last, "Reason: %s\r\n", _exception_string); + + output += seprintf(output, last, "Language: %s\r\n", _dynlang.curr_file); + +#ifdef _M_AMD64 + output += seprintf(output, last, "Exception %.8X at %.16IX\r\n" + "Registers:\r\n" + "RAX: %.16llX RBX: %.16llX RCX: %.16llX RDX: %.16llX\r\n" + "RSI: %.16llX RDI: %.16llX RBP: %.16llX RSP: %.16llX\r\n" + "R8: %.16llX R9: %.16llX R10: %.16llX R11: %.16llX\r\n" + "R12: %.16llX R13: %.16llX R14: %.16llX R15: %.16llX\r\n" + "RIP: %.16llX EFLAGS: %.8X\r\n" + "\r\nBytes at CS:RIP:\r\n", + ep->ExceptionRecord->ExceptionCode, + ep->ExceptionRecord->ExceptionAddress, + ep->ContextRecord->Rax, + ep->ContextRecord->Rbx, + ep->ContextRecord->Rcx, + ep->ContextRecord->Rdx, + ep->ContextRecord->Rsi, + ep->ContextRecord->Rdi, + ep->ContextRecord->Rbp, + ep->ContextRecord->Rsp, + ep->ContextRecord->R8, + ep->ContextRecord->R9, + ep->ContextRecord->R10, + ep->ContextRecord->R11, + ep->ContextRecord->R12, + ep->ContextRecord->R13, + ep->ContextRecord->R14, + ep->ContextRecord->R15, + ep->ContextRecord->Rip, + ep->ContextRecord->EFlags + ); +#else + output += seprintf(output, last, "Exception %.8X at %.8p\r\n" + "Registers:\r\n" + " EAX: %.8X EBX: %.8X ECX: %.8X EDX: %.8X\r\n" + " ESI: %.8X EDI: %.8X EBP: %.8X ESP: %.8X\r\n" + " EIP: %.8X EFLAGS: %.8X\r\n" + "\r\nBytes at CS:EIP:\r\n", + ep->ExceptionRecord->ExceptionCode, + ep->ExceptionRecord->ExceptionAddress, + ep->ContextRecord->Eax, + ep->ContextRecord->Ebx, + ep->ContextRecord->Ecx, + ep->ContextRecord->Edx, + ep->ContextRecord->Esi, + ep->ContextRecord->Edi, + ep->ContextRecord->Ebp, + ep->ContextRecord->Esp, + ep->ContextRecord->Eip, + ep->ContextRecord->EFlags + ); +#endif + + { +#ifdef _M_AMD64 + byte *b = (byte*)ep->ContextRecord->Rip; +#else + byte *b = (byte*)ep->ContextRecord->Eip; +#endif + int i; + for (i = 0; i != 24; i++) { + if (IsBadReadPtr(b, 1)) { + output += seprintf(output, last, " ??"); // OCR: WAS: , 0); + } else { + output += seprintf(output, last, " %.2X", *b); + } + b++; + } + output += seprintf(output, last, + "\r\n" + "\r\nStack trace: \r\n" + ); + } + + { + int i, j; +#ifdef _M_AMD64 + uint32 *b = (uint32*)ep->ContextRecord->Rsp; +#else + uint32 *b = (uint32*)ep->ContextRecord->Esp; +#endif + for (j = 0; j != 24; j++) { + for (i = 0; i != 8; i++) { + if (IsBadReadPtr(b, sizeof(uint32))) { + output += seprintf(output, last, " ????????"); // OCR: WAS - , 0); + } else { + output += seprintf(output, last, " %.8X", *b); + } + b++; + } + output += seprintf(output, last, "\r\n"); + } + } + + output += seprintf(output, last, "\r\nModule information:\r\n"); + output = PrintModuleList(output, last); + + { + _OSVERSIONINFOA os; + os.dwOSVersionInfoSize = sizeof(os); + GetVersionExA(&os); + output += seprintf(output, last, "\r\nSystem information:\r\n" + " Windows version %d.%d %d %s\r\n\r\n", + os.dwMajorVersion, os.dwMinorVersion, os.dwBuildNumber, os.szCSDVersion); + } + + _file_crash_log = CreateFile(_T("crash.log"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + + if (_file_crash_log != INVALID_HANDLE_VALUE) { + DWORD num_written; + WriteFile(_file_crash_log, _crash_msg, output - _crash_msg, &num_written, NULL); + } + +#if !defined(_DEBUG) + HMODULE dbghelp = LoadLibrary(_T("dbghelp.dll")); + if (dbghelp != NULL) { + typedef BOOL (WINAPI *MiniDumpWriteDump_t)(HANDLE, DWORD, HANDLE, + MINIDUMP_TYPE, + CONST PMINIDUMP_EXCEPTION_INFORMATION, + CONST PMINIDUMP_USER_STREAM_INFORMATION, + CONST PMINIDUMP_CALLBACK_INFORMATION); + MiniDumpWriteDump_t funcMiniDumpWriteDump = (MiniDumpWriteDump_t)GetProcAddress(dbghelp, "MiniDumpWriteDump"); + if (funcMiniDumpWriteDump != NULL) { + HANDLE file = CreateFile(_T("crash.dmp"), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + HANDLE proc = GetCurrentProcess(); + DWORD procid = GetCurrentProcessId(); + MINIDUMP_EXCEPTION_INFORMATION mdei; + MINIDUMP_USER_STREAM userstream; + MINIDUMP_USER_STREAM_INFORMATION musi; + char msg[] = "****** Built on " __DATE__ " " __TIME__ ". ******"; + + userstream.Type = LastReservedStream + 1; + userstream.Buffer = msg; + userstream.BufferSize = sizeof(msg); + + musi.UserStreamCount = 1; + musi.UserStreamArray = &userstream; + + mdei.ThreadId = GetCurrentThreadId(); + mdei.ExceptionPointers = ep; + mdei.ClientPointers = false; + + funcMiniDumpWriteDump(proc, procid, file, MiniDumpWithDataSegs, &mdei, &musi, NULL); + } + FreeLibrary(dbghelp); + } +#endif + + if (_file_crash_log != INVALID_HANDLE_VALUE) { + GamelogPrint(&GamelogPrintCrashLogProc); + CloseHandle(_file_crash_log); + } + + /* Close any possible log files */ + CloseConsoleLogIfActive(); + + if (_safe_esp) { +#ifdef _M_AMD64 + ep->ContextRecord->Rip = (DWORD64)Handler2; + ep->ContextRecord->Rsp = (DWORD64)_safe_esp; +#else + ep->ContextRecord->Eip = (DWORD)Handler2; + ep->ContextRecord->Esp = (DWORD)_safe_esp; +#endif + return EXCEPTION_CONTINUE_EXECUTION; + } + + + return EXCEPTION_EXECUTE_HANDLER; +} + +#ifdef _M_AMD64 +extern "C" void *_get_safe_esp(); +#endif + +static void Win32InitializeExceptions() +{ +#ifdef _M_AMD64 + _safe_esp = _get_safe_esp(); +#else + _asm { + mov _safe_esp, esp + } +#endif + + SetUnhandledExceptionFilter(ExceptionHandler); +} +#endif /* _MSC_VER */ + +/* Code below for windows version of opendir/readdir/closedir copied and + * modified from Jan Wassenberg's GPL implementation posted over at + * http://www.gamedev.net/community/forums/topic.asp?topic_id=364584&whichpage=1� */ + +/* suballocator - satisfies most requests with a reusable static instance. + * this avoids hundreds of alloc/free which would fragment the heap. + * To guarantee concurrency, we fall back to malloc if the instance is + * already in use (it's important to avoid suprises since this is such a + * low-level routine). */ +static DIR _global_dir; +static LONG _global_dir_is_in_use = false; + +static inline DIR *dir_calloc() +{ + DIR *d; + + if (InterlockedExchange(&_global_dir_is_in_use, true) == (LONG)true) { + d = CallocT<DIR>(1); + } else { + d = &_global_dir; + memset(d, 0, sizeof(*d)); + } + return d; +} + +static inline void dir_free(DIR *d) +{ + if (d == &_global_dir) { + _global_dir_is_in_use = (LONG)false; + } else { + free(d); + } +} + +DIR *opendir(const TCHAR *path) +{ + DIR *d; + UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box + DWORD fa = GetFileAttributes(path); + + if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) { + d = dir_calloc(); + if (d != NULL) { + TCHAR search_path[MAX_PATH]; + bool slash = path[_tcslen(path) - 1] == '\\'; + + /* build search path for FindFirstFile, try not to append additional slashes + * as it throws Win9x off its groove for root directories */ + _sntprintf(search_path, lengthof(search_path), _T("%s%s*"), path, slash ? _T("") : _T("\\")); + *lastof(search_path) = '\0'; + d->hFind = FindFirstFile(search_path, &d->fd); + + if (d->hFind != INVALID_HANDLE_VALUE || + GetLastError() == ERROR_NO_MORE_FILES) { // the directory is empty + d->ent.dir = d; + d->at_first_entry = true; + } else { + dir_free(d); + d = NULL; + } + } else { + errno = ENOMEM; + } + } else { + /* path not found or not a directory */ + d = NULL; + errno = ENOENT; + } + + SetErrorMode(sem); // restore previous setting + return d; +} + +struct dirent *readdir(DIR *d) +{ + DWORD prev_err = GetLastError(); // avoid polluting last error + + if (d->at_first_entry) { + /* the directory was empty when opened */ + if (d->hFind == INVALID_HANDLE_VALUE) return NULL; + d->at_first_entry = false; + } else if (!FindNextFile(d->hFind, &d->fd)) { // determine cause and bail + if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err); + return NULL; + } + + /* This entry has passed all checks; return information about it. + * (note: d_name is a pointer; see struct dirent definition) */ + d->ent.d_name = d->fd.cFileName; + return &d->ent; +} + +int closedir(DIR *d) +{ + FindClose(d->hFind); + dir_free(d); + return 0; +} + +bool FiosIsRoot(const char *file) +{ + return file[3] == '\0'; // C:\... +} + +void FiosGetDrives() +{ +#if defined(WINCE) + /* WinCE only knows one drive: / */ + FiosItem *fios = _fios_items.Append(); + fios->type = FIOS_TYPE_DRIVE; + fios->mtime = 0; + snprintf(fios->name, lengthof(fios->name), PATHSEP ""); + strecpy(fios->title, fios->name, lastof(fios->title)); +#else + TCHAR drives[256]; + const TCHAR *s; + + GetLogicalDriveStrings(lengthof(drives), drives); + for (s = drives; *s != '\0';) { + FiosItem *fios = _fios_items.Append(); + fios->type = FIOS_TYPE_DRIVE; + fios->mtime = 0; + snprintf(fios->name, lengthof(fios->name), "%c:", s[0] & 0xFF); + strecpy(fios->title, fios->name, lastof(fios->title)); + while (*s++ != '\0') { /* Nothing */ } + } +#endif +} + +bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb) +{ + /* hectonanoseconds between Windows and POSIX epoch */ + static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL; + const WIN32_FIND_DATA *fd = &ent->dir->fd; + + sb->st_size = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow; + /* UTC FILETIME to seconds-since-1970 UTC + * we just have to subtract POSIX epoch and scale down to units of seconds. + * http://www.gamedev.net/community/forums/topic.asp?topic_id=294070&whichpage=1� + * XXX - not entirely correct, since filetimes on FAT aren't UTC but local, + * this won't entirely be correct, but we use the time only for comparsion. */ + sb->st_mtime = (time_t)((*(uint64*)&fd->ftLastWriteTime - posix_epoch_hns) / 1E7); + sb->st_mode = (fd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)? S_IFDIR : S_IFREG; + + return true; +} + +bool FiosIsHiddenFile(const struct dirent *ent) +{ + return (ent->dir->fd.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0; +} + +bool FiosGetDiskFreeSpace(const char *path, uint64 *tot) +{ + UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box + bool retval = false; + TCHAR root[4]; + DWORD spc, bps, nfc, tnc; + + _sntprintf(root, lengthof(root), _T("%c:") _T(PATHSEP), path[0]); + if (tot != NULL && GetDiskFreeSpace(root, &spc, &bps, &nfc, &tnc)) { + *tot = ((spc * bps) * (uint64)nfc); + retval = true; + } + + SetErrorMode(sem); // reset previous setting + return retval; +} + +static int ParseCommandLine(char *line, char **argv, int max_argc) +{ + int n = 0; + + do { + /* skip whitespace */ + while (*line == ' ' || *line == '\t') line++; + + /* end? */ + if (*line == '\0') break; + + /* special handling when quoted */ + if (*line == '"') { + argv[n++] = ++line; + while (*line != '"') { + if (*line == '\0') return n; + line++; + } + } else { + argv[n++] = line; + while (*line != ' ' && *line != '\t') { + if (*line == '\0') return n; + line++; + } + } + *line++ = '\0'; + } while (n != max_argc); + + return n; +} + +void CreateConsole() +{ +#if defined(WINCE) + /* WinCE doesn't support console stuff */ +#else + HANDLE hand; + CONSOLE_SCREEN_BUFFER_INFO coninfo; + + if (_has_console) return; + _has_console = true; + + AllocConsole(); + + hand = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(hand, &coninfo); + coninfo.dwSize.Y = 500; + SetConsoleScreenBufferSize(hand, coninfo.dwSize); + + /* redirect unbuffered STDIN, STDOUT, STDERR to the console */ +#if !defined(__CYGWIN__) + *stdout = *_fdopen( _open_osfhandle((intptr_t)hand, _O_TEXT), "w" ); + *stdin = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT), "r" ); + *stderr = *_fdopen(_open_osfhandle((intptr_t)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT), "w" ); +#else + /* open_osfhandle is not in cygwin */ + *stdout = *fdopen(1, "w" ); + *stdin = *fdopen(0, "r" ); + *stderr = *fdopen(2, "w" ); +#endif + + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); +#endif +} + +void ShowInfo(const char *str) +{ + if (_has_console) { + fprintf(stderr, "%s\n", str); + } else { + bool old; +#if defined(UNICODE) + /* We need to put the text in a seperate buffer because the default + * buffer in MB_TO_WIDE might not be large enough (512 chars) */ + wchar_t help_msgW[8192]; +#endif + ReleaseCapture(); + _left_button_clicked = _left_button_down = false; + + old = MyShowCursor(true); + if (MessageBox(GetActiveWindow(), MB_TO_WIDE_BUFFER(str, help_msgW, lengthof(help_msgW)), _T("OpenTTD"), MB_ICONINFORMATION | MB_OKCANCEL) == IDCANCEL) { + CreateConsole(); + } + MyShowCursor(old); + } +} + +#if defined(WINCE) +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) +#else +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +#endif +{ + int argc; + char *argv[64]; // max 64 command line arguments + char *cmdline; + +#if !defined(UNICODE) + _codepage = GetACP(); // get system codepage as some kind of a default +#endif /* UNICODE */ + +#if defined(UNICODE) + +#if !defined(WINCE) + /* Check if a win9x user started the win32 version */ + if (HasBit(GetVersion(), 31)) usererror("This version of OpenTTD doesn't run on windows 95/98/ME.\nPlease download the win9x binary and try again."); +#endif + + /* For UNICODE we need to convert the commandline to char* _AND_ + * save it because argv[] points into this buffer and thus needs to + * be available between subsequent calls to FS2OTTD() */ + char cmdlinebuf[MAX_PATH]; +#endif /* UNICODE */ + + cmdline = WIDE_TO_MB_BUFFER(GetCommandLine(), cmdlinebuf, lengthof(cmdlinebuf)); + +#if defined(_DEBUG) + CreateConsole(); +#endif + +#if !defined(WINCE) + _set_error_mode(_OUT_TO_MSGBOX); // force assertion output to messagebox +#endif + + /* setup random seed to something quite random */ + SetRandomSeed(GetTickCount()); + + argc = ParseCommandLine(cmdline, argv, lengthof(argv)); + +#if defined(WIN32_EXCEPTION_TRACKER) + Win32InitializeExceptions(); +#endif + +#if defined(WIN32_EXCEPTION_TRACKER_DEBUG) + _try { + LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep); +#endif + ttd_main(argc, argv); + +#if defined(WIN32_EXCEPTION_TRACKER_DEBUG) + } _except (ExceptionHandler(_exception_info())) {} +#endif + + return 0; +} + +#if defined(WINCE) +void GetCurrentDirectoryW(int length, wchar_t *path) +{ + /* Get the name of this module */ + GetModuleFileName(NULL, path, length); + + /* Remove the executable name, this we call CurrentDir */ + wchar_t *pDest = wcsrchr(path, '\\'); + if (pDest != NULL) { + int result = pDest - path + 1; + path[result] = '\0'; + } +} +#endif + +char *getcwd(char *buf, size_t size) +{ +#if defined(WINCE) + TCHAR path[MAX_PATH]; + GetModuleFileName(NULL, path, MAX_PATH); + convert_from_fs(path, buf, size); + /* GetModuleFileName returns dir with file, so remove everything behind latest '\\' */ + char *p = strrchr(buf, '\\'); + if (p != NULL) *p = '\0'; +#elif defined(UNICODE) + TCHAR path[MAX_PATH]; + GetCurrentDirectory(MAX_PATH - 1, path); + convert_from_fs(path, buf, size); +#else + GetCurrentDirectory(size, buf); +#endif + return buf; +} + + +void DetermineBasePaths(const char *exe) +{ + char tmp[MAX_PATH]; + TCHAR path[MAX_PATH]; +#ifdef WITH_PERSONAL_DIR + SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path); + strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp)); + AppendPathSeparator(tmp, MAX_PATH); + ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH); + AppendPathSeparator(tmp, MAX_PATH); + _searchpaths[SP_PERSONAL_DIR] = strdup(tmp); + + SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path); + strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp)); + AppendPathSeparator(tmp, MAX_PATH); + ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH); + AppendPathSeparator(tmp, MAX_PATH); + _searchpaths[SP_SHARED_DIR] = strdup(tmp); +#else + _searchpaths[SP_PERSONAL_DIR] = NULL; + _searchpaths[SP_SHARED_DIR] = NULL; +#endif + + /* Get the path to working directory of OpenTTD */ + getcwd(tmp, lengthof(tmp)); + AppendPathSeparator(tmp, MAX_PATH); + _searchpaths[SP_WORKING_DIR] = strdup(tmp); + + if (!GetModuleFileName(NULL, path, lengthof(path))) { + DEBUG(misc, 0, "GetModuleFileName failed (%lu)\n", GetLastError()); + _searchpaths[SP_BINARY_DIR] = NULL; + } else { + TCHAR exec_dir[MAX_PATH]; + _tcsncpy(path, MB_TO_WIDE_BUFFER(exe, path, lengthof(path)), lengthof(path)); + if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, NULL)) { + DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError()); + _searchpaths[SP_BINARY_DIR] = NULL; + } else { + strecpy(tmp, WIDE_TO_MB_BUFFER(exec_dir, tmp, lengthof(tmp)), lastof(tmp)); + char *s = strrchr(tmp, PATHSEPCHAR); + *(s + 1) = '\0'; + _searchpaths[SP_BINARY_DIR] = strdup(tmp); + } + } + + _searchpaths[SP_INSTALLATION_DIR] = NULL; + _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL; +} + +/** + * Insert a chunk of text from the clipboard onto the textbuffer. Get TEXT clipboard + * and append this up to the maximum length (either absolute or screenlength). If maxlength + * is zero, we don't care about the screenlength but only about the physical length of the string + * @param tb Textbuf type to be changed + * @return true on successful change of Textbuf, or false otherwise + */ +bool InsertTextBufferClipboard(Textbuf *tb) +{ + HGLOBAL cbuf; + char utf8_buf[512]; + const char *ptr; + + WChar c; + uint16 width, length; + + if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { + OpenClipboard(NULL); + cbuf = GetClipboardData(CF_UNICODETEXT); + + ptr = (const char*)GlobalLock(cbuf); + const char *ret = convert_from_fs((wchar_t*)ptr, utf8_buf, lengthof(utf8_buf)); + GlobalUnlock(cbuf); + CloseClipboard(); + + if (*ret == '\0') return false; +#if !defined(UNICODE) + } else if (IsClipboardFormatAvailable(CF_TEXT)) { + OpenClipboard(NULL); + cbuf = GetClipboardData(CF_TEXT); + + ptr = (const char*)GlobalLock(cbuf); + strecpy(utf8_buf, FS2OTTD(ptr), lastof(utf8_buf)); + + GlobalUnlock(cbuf); + CloseClipboard(); +#endif /* UNICODE */ + } else { + return false; + } + + width = length = 0; + + for (ptr = utf8_buf; (c = Utf8Consume(&ptr)) != '\0';) { + if (!IsPrintable(c)) break; + + byte len = Utf8CharLen(c); + if (tb->size + length + len > tb->maxsize) break; + + byte charwidth = GetCharacterWidth(FS_NORMAL, c); + if (tb->maxwidth != 0 && width + tb->width + charwidth > tb->maxwidth) break; + + width += charwidth; + length += len; + } + + if (length == 0) return false; + + memmove(tb->buf + tb->caretpos + length, tb->buf + tb->caretpos, tb->size - tb->caretpos); + memcpy(tb->buf + tb->caretpos, utf8_buf, length); + tb->width += width; + tb->caretxoffs += width; + + tb->size += length; + tb->caretpos += length; + assert(tb->size <= tb->maxsize); + tb->buf[tb->size - 1] = '\0'; // terminating zero + + return true; +} + + +void CSleep(int milliseconds) +{ + Sleep(milliseconds); +} + + +/** Utility function to get the current timestamp in milliseconds + * Useful for profiling */ +int64 GetTS() +{ + static double freq; + __int64 value; + if (!freq) { + QueryPerformanceFrequency((LARGE_INTEGER*)&value); + freq = (double)1000000 / value; + } + QueryPerformanceCounter((LARGE_INTEGER*)&value); + return (__int64)(value * freq); +} + + +/** + * Convert to OpenTTD's encoding from that of the local environment. + * When the project is built in UNICODE, the system codepage is irrelevant and + * the input string is wide. In ANSI mode, the string is in the + * local codepage which we'll convert to wide-char, and then to UTF-8. + * OpenTTD internal encoding is UTF8. + * The returned value's contents can only be guaranteed until the next call to + * this function. So if the value is needed for anything else, use convert_from_fs + * @param name pointer to a valid string that will be converted (local, or wide) + * @return pointer to the converted string; if failed string is of zero-length + * @see the current code-page comes from video\win32_v.cpp, event-notification + * WM_INPUTLANGCHANGE */ +const char *FS2OTTD(const TCHAR *name) +{ + static char utf8_buf[512]; +#if defined(UNICODE) + return convert_from_fs(name, utf8_buf, lengthof(utf8_buf)); +#else + char *s = utf8_buf; + + for (; *name != '\0'; name++) { + wchar_t w; + int len = MultiByteToWideChar(_codepage, 0, name, 1, &w, 1); + if (len != 1) { + DEBUG(misc, 0, "[utf8] M2W error converting '%c'. Errno %lu", *name, GetLastError()); + continue; + } + + if (s + Utf8CharLen(w) >= lastof(utf8_buf)) break; + s += Utf8Encode(s, w); + } + + *s = '\0'; + return utf8_buf; +#endif /* UNICODE */ +} + +/** + * Convert from OpenTTD's encoding to that of the local environment. + * When the project is built in UNICODE the system codepage is irrelevant and + * the converted string is wide. In ANSI mode, the UTF8 string is converted + * to multi-byte. + * OpenTTD internal encoding is UTF8. + * The returned value's contents can only be guaranteed until the next call to + * this function. So if the value is needed for anything else, use convert_from_fs + * @param name pointer to a valid string that will be converted (UTF8) + * @return pointer to the converted string; if failed string is of zero-length + * @see the current code-page comes from video\win32_v.cpp, event-notification + * WM_INPUTLANGCHANGE */ +const TCHAR *OTTD2FS(const char *name) +{ + static TCHAR system_buf[512]; +#if defined(UNICODE) + return convert_to_fs(name, system_buf, lengthof(system_buf)); +#else + char *s = system_buf; + + for (WChar c; (c = Utf8Consume(&name)) != '\0';) { + if (s >= lastof(system_buf)) break; + + char mb; + int len = WideCharToMultiByte(_codepage, 0, (wchar_t*)&c, 1, &mb, 1, NULL, NULL); + if (len != 1) { + DEBUG(misc, 0, "[utf8] W2M error converting '0x%X'. Errno %lu", c, GetLastError()); + continue; + } + + *s++ = mb; + } + + *s = '\0'; + return system_buf; +#endif /* UNICODE */ +} + + +/** Convert to OpenTTD's encoding from that of the environment in + * UNICODE. OpenTTD encoding is UTF8, local is wide + * @param name pointer to a valid string that will be converted + * @param utf8_buf pointer to a valid buffer that will receive the converted string + * @param buflen length in characters of the receiving buffer + * @return pointer to utf8_buf. If conversion fails the string is of zero-length */ +char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen) +{ + int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, (int)buflen, NULL, NULL); + if (len == 0) { + DEBUG(misc, 0, "[utf8] W2M error converting wide-string. Errno %lu", GetLastError()); + utf8_buf[0] = '\0'; + } + + return utf8_buf; +} + + +/** Convert from OpenTTD's encoding to that of the environment in + * UNICODE. OpenTTD encoding is UTF8, local is wide + * @param name pointer to a valid string that will be converted + * @param utf16_buf pointer to a valid wide-char buffer that will receive the + * converted string + * @param buflen length in wide characters of the receiving buffer + * @return pointer to utf16_buf. If conversion fails the string is of zero-length */ +wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen) +{ + int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, utf16_buf, (int)buflen); + if (len == 0) { + DEBUG(misc, 0, "[utf8] M2W error converting '%s'. Errno %lu", name, GetLastError()); + utf16_buf[0] = '\0'; + } + + return utf16_buf; +} + +/** Our very own SHGetFolderPath function for support of windows operating + * systems that don't have this function (eg Win9x, etc.). We try using the + * native function, and if that doesn't exist we will try a more crude approach + * of environment variables and hope for the best */ +HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath) +{ + static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) = NULL; + static bool first_time = true; + + /* We only try to load the library one time; if it fails, it fails */ + if (first_time) { +#if defined(UNICODE) +# define W(x) x "W" +#else +# define W(x) x "A" +#endif + if (!LoadLibraryList((Function*)&SHGetFolderPath, "SHFolder.dll\0" W("SHGetFolderPath") "\0\0")) { + DEBUG(misc, 0, "Unable to load " W("SHGetFolderPath") "from SHFolder.dll"); + } +#undef W + first_time = false; + } + + if (SHGetFolderPath != NULL) return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath); + + /* SHGetFolderPath doesn't exist, try a more conservative approach, + * eg environment variables. This is only included for legacy modes + * MSDN says: that 'pszPath' is a "Pointer to a null-terminated string of + * length MAX_PATH which will receive the path" so let's assume that + * Windows 95 with Internet Explorer 5.0, Windows 98 with Internet Explorer 5.0, + * Windows 98 Second Edition (SE), Windows NT 4.0 with Internet Explorer 5.0, + * Windows NT 4.0 with Service Pack 4 (SP4) */ + { + DWORD ret; + switch (csidl) { + case CSIDL_FONTS: // Get the system font path, eg %WINDIR%\Fonts + ret = GetEnvironmentVariable(_T("WINDIR"), pszPath, MAX_PATH); + if (ret == 0) break; + _tcsncat(pszPath, _T("\\Fonts"), MAX_PATH); + + return (HRESULT)0; + break; + /* XXX - other types to go here when needed... */ + } + } + + return E_INVALIDARG; +} + +/** Determine the current user's locale. */ +const char *GetCurrentLocale(const char *) +{ + char lang[9], country[9]; + if (GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, lengthof(lang)) == 0 || + GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, country, lengthof(country)) == 0) { + /* Unable to retrieve the locale. */ + return NULL; + } + /* Format it as 'en_us'. */ + static char retbuf[6] = {lang[0], lang[1], '_', country[0], country[1], 0}; + return retbuf; +} diff --git a/src/os/windows/win32.h b/src/os/windows/win32.h new file mode 100644 index 000000000..19f04d1f2 --- /dev/null +++ b/src/os/windows/win32.h @@ -0,0 +1,55 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file win32.h declarations of functions for MS windows systems */ + +#ifndef WIN32_H +#define WIN32_H + +#include <windows.h> +bool MyShowCursor(bool show); + +typedef void (*Function)(int); +bool LoadLibraryList(Function proc[], const char *dll); + +char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen); +wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen); + +/* Function shortcuts for UTF-8 <> UNICODE conversion. When unicode is not + * defined these macros return the string passed to them, with UNICODE + * they return a pointer to the converted string. The only difference between + * XX_TO_YY and XX_TO_YY_BUFFER is that with the buffer variant you can + * specify where to put the converted string (and how long it can be). Without + * the buffer and internal buffer is used, of max 512 characters */ +#if defined(UNICODE) +# define MB_TO_WIDE(str) OTTD2FS(str) +# define MB_TO_WIDE_BUFFER(str, buffer, buflen) convert_to_fs(str, buffer, buflen) +# define WIDE_TO_MB(str) FS2OTTD(str) +# define WIDE_TO_MB_BUFFER(str, buffer, buflen) convert_from_fs(str, buffer, buflen) +#else +extern uint _codepage; // local code-page in the system @see win32_v.cpp:WM_INPUTLANGCHANGE +# define MB_TO_WIDE(str) (str) +# define MB_TO_WIDE_BUFFER(str, buffer, buflen) (str) +# define WIDE_TO_MB(str) (str) +# define WIDE_TO_MB_BUFFER(str, buffer, buflen) (str) +#endif + +/* Override SHGetFolderPath with our custom implementation */ +#if defined(SHGetFolderPath) +#undef SHGetFolderPath +#endif +#define SHGetFolderPath OTTDSHGetFolderPath + +HRESULT OTTDSHGetFolderPath(HWND, int, HANDLE, DWORD, LPTSTR); + +#if defined(__MINGW32__) +#define SHGFP_TYPE_CURRENT 0 +#endif /* __MINGW32__ */ + +#endif /* WIN32_H */ diff --git a/src/os/windows/win64.asm b/src/os/windows/win64.asm new file mode 100644 index 000000000..d95bc3898 --- /dev/null +++ b/src/os/windows/win64.asm @@ -0,0 +1,8 @@ + .CODE + +PUBLIC _get_safe_esp +_get_safe_esp: + MOV RAX,RSP + RET + + END |