# HG changeset patch # User Darkvater # Date 1164724951 0 # Node ID 9cfb93a953ffa531d6d13461a5a24aaa40f74440 # Parent fb9d6ac24a755e049a40b3ded166ed074517c0fb (svn r7274) -Codechange [utf8]: Add input/output unicode support. This actually only changes win32 since it uses UTF16 for file access. To keep os-specific code to a minimum, OpenTTD uses UTF8 internally everywhere, converting to OS-type when needed (save/load/screenshot/etc.) diff --git a/fios.c b/fios.c --- a/fios.c +++ b/fios.c @@ -170,12 +170,16 @@ snprintf(buf, size, "%s" PATHSEP "%s%s", _fios_path, name, extension); } +#if defined(WIN32) || defined(WIN64) +# define unlink _wunlink +#endif + bool FiosDelete(const char *name) { char filename[512]; FiosMakeSavegameName(filename, name, lengthof(filename)); - return unlink(filename) == 0; + return unlink(OTTD2FS(filename)) == 0; } bool FileExists(const char *filename) @@ -210,14 +214,16 @@ /* Show subdirectories */ if (mode != SLD_NEW_GAME && (dir = opendir(_fios_path)) != NULL) { while ((dirent = readdir(dir)) != NULL) { + const char *d_name = FS2OTTD(dirent->d_name); + /* found file must be directory, but not '.' or '..' */ if (FiosIsValidFile(_fios_path, dirent, &sb) && (sb.st_mode & S_IFDIR) && - strcmp(dirent->d_name, ".") != 0 && strcmp(dirent->d_name, "..") != 0) { + strcmp(d_name, ".") != 0 && strcmp(d_name, "..") != 0) { fios = FiosAlloc(); fios->type = FIOS_TYPE_DIR; fios->mtime = 0; - ttd_strlcpy(fios->name, dirent->d_name, lengthof(fios->name)); - snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", FS2OTTD(dirent->d_name)); + ttd_strlcpy(fios->name, d_name, lengthof(fios->name)); + snprintf(fios->title, lengthof(fios->title), "%s" PATHSEP " (Directory)", d_name); str_validate(fios->title); } } @@ -241,25 +247,26 @@ while ((dirent = readdir(dir)) != NULL) { char fios_title[64]; char *t; + char *d_name = (char*)FS2OTTD(dirent->d_name); byte type; if (!FiosIsValidFile(_fios_path, dirent, &sb) || !(sb.st_mode & S_IFREG)) continue; /* File has no extension, skip it */ - if ((t = strrchr(dirent->d_name, '.')) == NULL) continue; + if ((t = strrchr(d_name, '.')) == NULL) continue; fios_title[0] = '\0'; // reset the title; - type = callback_proc(mode, dirent->d_name, t, fios_title); + type = callback_proc(mode, d_name, t, fios_title); if (type != FIOS_TYPE_INVALID) { fios = FiosAlloc(); fios->mtime = sb.st_mtime; fios->type = type; - ttd_strlcpy(fios->name, dirent->d_name, lengthof(fios->name)); + ttd_strlcpy(fios->name, d_name, lengthof(fios->name)); /* Some callbacks want to lookup the title of the file. Allow that. * If we just copy the title from the filename, strip the extension */ - t = (fios_title[0] == '\0') ? *t = '\0', dirent->d_name : fios_title; - ttd_strlcpy(fios->title, FS2OTTD(t), lengthof(fios->title)); + t = (fios_title[0] == '\0') ? *t = '\0', d_name : fios_title; + ttd_strlcpy(fios->title, t, lengthof(fios->title)); str_validate(fios->title); } } diff --git a/fios.h b/fios.h --- a/fios.h +++ b/fios.h @@ -57,7 +57,7 @@ typedef struct DIR DIR; typedef struct dirent { // XXX - only d_name implemented - char *d_name; /* name of found file */ + wchar_t *d_name; /* name of found file */ /* little hack which will point to parent DIR struct which will * save us a call to GetFileAttributes if we want information * about the file (for example in function fio_bla */ @@ -70,7 +70,7 @@ * note: having only one global instance is not possible because * multiple independent opendir/readdir sequences must be supported. */ dirent ent; - WIN32_FIND_DATA fd; + WIN32_FIND_DATAW fd; /* since opendir calls FindFirstFile, we need a means of telling the * first call to readdir that we already have a file. * that's the case iff this is true */ diff --git a/hal.h b/hal.h --- a/hal.h +++ b/hal.h @@ -46,12 +46,4 @@ void CreateConsole(void); -#if defined(WIN32) || defined(WIN64) || defined(__WATCOMC__) -# define FS2OTTD(name) name -# define OTTD2FS(name) name -#else -const char *FS2OTTD(const char *name); -const char *OTTD2FS(const char *name); -#endif - #endif /* HAL_H */ diff --git a/misc_gui.c b/misc_gui.c --- a/misc_gui.c +++ b/misc_gui.c @@ -1486,7 +1486,7 @@ if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) break; if (IsWindowWidgetLowered(w, 11)) { /* Delete button clicked */ - if (!FiosDelete(OTTD2FS(WP(w,querystr_d).text.buf))) { + if (!FiosDelete(WP(w,querystr_d).text.buf)) { ShowErrorMessage(INVALID_STRING_ID, STR_4008_UNABLE_TO_DELETE_FILE, 0, 0); } else { BuildFileList(); diff --git a/os2.c b/os2.c --- a/os2.c +++ b/os2.c @@ -261,3 +261,6 @@ { delay(milliseconds); } + +const char *FS2OTTD(const char *name) {return name;} +const char *OTTD2FS(const char *name) {return name;} diff --git a/saveload.c b/saveload.c --- a/saveload.c +++ b/saveload.c @@ -1516,7 +1516,7 @@ return SL_OK; } - _sl.fh = (mode == SL_SAVE) ? fopen(OTTD2FS(filename), "wb") : fopen(filename, "rb"); + _sl.fh = (mode == SL_SAVE) ? fopen(filename, "wb") : fopen(filename, "rb"); if (_sl.fh == NULL) { DEBUG(misc, 0) ("[Sl] Cannot open savegame for saving/loading."); return SL_ERROR; diff --git a/screenshot.c b/screenshot.c --- a/screenshot.c +++ b/screenshot.c @@ -76,7 +76,7 @@ if (pixelformat != 8) return false; - f = fopen(OTTD2FS(name), "wb"); + f = fopen(name, "wb"); if (f == NULL) return false; // each scanline must be aligned on a 32bit boundary @@ -180,7 +180,7 @@ if (pixelformat != 8) return false; - f = fopen(OTTD2FS(name), "wb"); + f = fopen(name, "wb"); if (f == NULL) return false; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (char *)name, png_my_error, png_my_warning); @@ -292,7 +292,7 @@ if (pixelformat != 8 || w == 0) return false; - f = fopen(OTTD2FS(name), "wb"); + f = fopen(name, "wb"); if (f == NULL) return false; memset(&pcx, 0, sizeof(pcx)); diff --git a/stdafx.h b/stdafx.h --- a/stdafx.h +++ b/stdafx.h @@ -154,6 +154,19 @@ # include #endif /* defined(_MSC_VER) */ +/* NOTE: the string returned by these functions is only valid until the next + * call to the same function and is not thread- or reentrancy-safe */ +#if !defined(STRGEN) +# if defined(WIN32) || defined(WIN64) +# define fopen(file, mode) _wfopen(OTTD2FS(file), L ## mode) + const char *FS2OTTD(const wchar_t *name); + const wchar_t *OTTD2FS(const char *name); +# else +# define fopen(file, mode) fopen(OTTD2FS(file), mode) + const char *FS2OTTD(const char *name); + const char *OTTD2FS(const char *name); +# endif /* WIN32 */ +#endif /* STRGEN */ // Windows has always LITTLE_ENDIAN #if defined(WIN32) || defined(__OS2__) || defined(WIN64) diff --git a/strings.c b/strings.c --- a/strings.c +++ b/strings.c @@ -1228,10 +1228,11 @@ dir = opendir(_path.lang_dir); if (dir != NULL) { while ((dirent = readdir(dir)) != NULL) { - char *t = strrchr(dirent->d_name, '.'); + const char *d_name = FS2OTTD(dirent->d_name); + char *t = strrchr(d_name, '.'); if (t != NULL && strcmp(t, ".lng") == 0) { - languages[num++] = strdup(dirent->d_name); + languages[num++] = strdup(d_name); if (num == max) break; } } diff --git a/win32.c b/win32.c --- a/win32.c +++ b/win32.c @@ -639,7 +639,7 @@ { DIR *d; UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box - DWORD fa = GetFileAttributes(path); + DWORD fa = GetFileAttributesW(OTTD2FS(path)); if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) { d = dir_calloc(); @@ -647,7 +647,7 @@ char search_path[MAX_PATH]; /* build search path for FindFirstFile */ snprintf(search_path, lengthof(search_path), "%s" PATHSEP "*", path); - d->hFind = FindFirstFile(search_path, &d->fd); + d->hFind = FindFirstFileW(OTTD2FS(search_path), &d->fd); if (d->hFind != INVALID_HANDLE_VALUE || GetLastError() == ERROR_NO_MORE_FILES) { // the directory is empty @@ -678,7 +678,7 @@ /* 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 + } else if (!FindNextFileW(d->hFind, &d->fd)) { // determine cause and bail if (GetLastError() == ERROR_NO_MORE_FILES) SetLastError(prev_err); return NULL; } @@ -721,7 +721,7 @@ { // hectonanoseconds between Windows and POSIX epoch static const int64 posix_epoch_hns = 0x019DB1DED53E8000LL; - const WIN32_FIND_DATA *fd = &ent->dir->fd; + const WIN32_FIND_DATAW *fd = &ent->dir->fd; if (fd->dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) return false; sb->st_size = ((uint64) fd->nFileSizeHigh << 32) + fd->nFileSizeLow; @@ -886,11 +886,12 @@ void DeterminePaths(void) { - char *s; - char *cfg; + char *s, *cfg; + wchar_t path[MAX_PATH]; _path.personal_dir = _path.game_data_dir = cfg = malloc(MAX_PATH); - GetCurrentDirectory(MAX_PATH - 1, cfg); + GetCurrentDirectoryW(MAX_PATH - 1, path); + WideCharToMultiByte(CP_UTF8, 0, path, -1, cfg, MAX_PATH, NULL, NULL); cfg[0] = toupper(cfg[0]); s = strchr(cfg, '\0'); @@ -911,10 +912,10 @@ _log_file = str_fmt("%sopenttd.log", _path.personal_dir); // make (auto)save and scenario folder - CreateDirectory(_path.save_dir, NULL); - CreateDirectory(_path.autosave_dir, NULL); - CreateDirectory(_path.scenario_dir, NULL); - CreateDirectory(_path.heightmap_dir, NULL); + CreateDirectoryW(OTTD2FS(_path.save_dir), NULL); + CreateDirectoryW(OTTD2FS(_path.autosave_dir), NULL); + CreateDirectoryW(OTTD2FS(_path.scenario_dir), NULL); + CreateDirectoryW(OTTD2FS(_path.heightmap_dir), NULL); } /** @@ -1009,3 +1010,38 @@ QueryPerformanceCounter((LARGE_INTEGER*)&value); return (__int64)(value * freq); } + +/** Convert from OpenTTD's encoding to that of the local environment + * First convert from UTF8 to wide-char, then to local + * @param name pointer to a valid string that will be converted + * @return pointer to a new stringbuffer that contains the converted string */ +const wchar_t *OTTD2FS(const char *name) +{ + static wchar_t ucs2_buf[MAX_PATH]; + int len; + + len = MultiByteToWideChar(CP_UTF8, 0, name, -1, ucs2_buf, lengthof(ucs2_buf)); + if (len == 0) { + DEBUG(misc, 0) ("[utf8] Error converting '%s'. Errno %d", name, GetLastError()); + return L""; + } + + return (const wchar_t*)ucs2_buf; +} + +/** 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 wchar_t *name) +{ + static char utf8_buf[512]; + int len; + + len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, lengthof(utf8_buf), NULL, NULL); + if (len == 0) { + DEBUG(misc, 0) ("[utf8] Error converting string. Errno %d", GetLastError()); + return ""; + } + + return (const char*)utf8_buf; +}