mirror of https://github.com/OpenTTD/OpenTTD.git
(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.)
This commit is contained in:
parent
9253387001
commit
4e71091137
25
fios.c
25
fios.c
|
@ -170,12 +170,16 @@ void FiosMakeSavegameName(char *buf, const char *name, size_t size)
|
|||
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 @@ static FiosItem *FiosGetFileList(int mode, fios_getlist_callback_proc *callback_
|
|||
/* 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 @@ static FiosItem *FiosGetFileList(int mode, fios_getlist_callback_proc *callback_
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
4
fios.h
4
fios.h
|
@ -57,7 +57,7 @@ int CDECL compare_FiosItems(const void *a, const void *b);
|
|||
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 @@ struct DIR {
|
|||
* 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 */
|
||||
|
|
8
hal.h
8
hal.h
|
@ -46,12 +46,4 @@ void GameLoop(void);
|
|||
|
||||
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 */
|
||||
|
|
|
@ -1486,7 +1486,7 @@ static void SaveLoadDlgWndProc(Window *w, WindowEvent *e)
|
|||
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();
|
||||
|
|
3
os2.c
3
os2.c
|
@ -261,3 +261,6 @@ void CSleep(int milliseconds)
|
|||
{
|
||||
delay(milliseconds);
|
||||
}
|
||||
|
||||
const char *FS2OTTD(const char *name) {return name;}
|
||||
const char *OTTD2FS(const char *name) {return name;}
|
||||
|
|
|
@ -1516,7 +1516,7 @@ SaveOrLoadResult SaveOrLoad(const char *filename, int mode)
|
|||
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;
|
||||
|
|
|
@ -76,7 +76,7 @@ static bool MakeBmpImage(const char *name, ScreenshotCallback *callb, void *user
|
|||
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 @@ static bool MakePNGImage(const char *name, ScreenshotCallback *callb, void *user
|
|||
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 @@ static bool MakePCXImage(const char *name, ScreenshotCallback *callb, void *user
|
|||
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));
|
||||
|
|
13
stdafx.h
13
stdafx.h
|
@ -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)
|
||||
|
|
|
@ -1228,10 +1228,11 @@ static int GetLanguageList(char **languages, int max)
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
58
win32.c
58
win32.c
|
@ -639,7 +639,7 @@ DIR *opendir(const char *path)
|
|||
{
|
||||
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 @@ DIR *opendir(const char *path)
|
|||
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 @@ struct dirent *readdir(DIR *d)
|
|||
/* 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 @@ 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;
|
||||
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 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
|
|||
|
||||
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 @@ void DeterminePaths(void)
|
|||
_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 @@ int64 GetTS(void)
|
|||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue