(svn r7274) -Codechange [utf8]: Add input/output unicode support. This actually only changes win32
authorDarkvater
Tue, 28 Nov 2006 14:42:31 +0000
changeset 5167 2b9d61386688
parent 5166 c40f2b19eca9
child 5168 10a8dc9788d7
(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.)
fios.c
fios.h
hal.h
misc_gui.c
os2.c
saveload.c
screenshot.c
stdafx.h
strings.c
win32.c
--- a/fios.c	Tue Nov 28 14:32:24 2006 +0000
+++ b/fios.c	Tue Nov 28 14:42:31 2006 +0000
@@ -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);
 			}
 		}
--- a/fios.h	Tue Nov 28 14:32:24 2006 +0000
+++ b/fios.h	Tue Nov 28 14:42:31 2006 +0000
@@ -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 */
--- a/hal.h	Tue Nov 28 14:32:24 2006 +0000
+++ b/hal.h	Tue Nov 28 14:42:31 2006 +0000
@@ -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 */
--- a/misc_gui.c	Tue Nov 28 14:32:24 2006 +0000
+++ b/misc_gui.c	Tue Nov 28 14:42:31 2006 +0000
@@ -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();
--- a/os2.c	Tue Nov 28 14:32:24 2006 +0000
+++ b/os2.c	Tue Nov 28 14:42:31 2006 +0000
@@ -261,3 +261,6 @@
 {
 	delay(milliseconds);
 }
+
+const char *FS2OTTD(const char *name) {return name;}
+const char *OTTD2FS(const char *name) {return name;}
--- a/saveload.c	Tue Nov 28 14:32:24 2006 +0000
+++ b/saveload.c	Tue Nov 28 14:42:31 2006 +0000
@@ -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;
--- a/screenshot.c	Tue Nov 28 14:32:24 2006 +0000
+++ b/screenshot.c	Tue Nov 28 14:42:31 2006 +0000
@@ -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));
--- a/stdafx.h	Tue Nov 28 14:32:24 2006 +0000
+++ b/stdafx.h	Tue Nov 28 14:42:31 2006 +0000
@@ -154,6 +154,19 @@
 # include <stddef.h>
 #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)
--- a/strings.c	Tue Nov 28 14:32:24 2006 +0000
+++ b/strings.c	Tue Nov 28 14:42:31 2006 +0000
@@ -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;
 			}
 		}
--- a/win32.c	Tue Nov 28 14:32:24 2006 +0000
+++ b/win32.c	Tue Nov 28 14:42:31 2006 +0000
@@ -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;
+}