From 7ab4c928ef4511ea5753a36a57c3506d9fd5086b Mon Sep 17 00:00:00 2001 From: Henrik Gramner Date: Sat, 12 Sep 2020 19:24:00 +0200 Subject: [PATCH] Add support for long filenames on Windows 10 --- Makefile | 3 +- common/osdep.h | 132 ++++++++++++++++++++++++++++++++--------------- input/avs.c | 58 +++++++++++++++------ x264.c | 2 +- x264res.manifest | 10 ++++ x264res.rc | 4 ++ 6 files changed, 150 insertions(+), 59 deletions(-) create mode 100644 x264res.manifest diff --git a/Makefile b/Makefile index 730c4cd4..6b2cfad7 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ vpath %.h $(SRCPATH) vpath %.S $(SRCPATH) vpath %.asm $(SRCPATH) vpath %.rc $(SRCPATH) +vpath %.manifest $(SRCPATH) CFLAGS += $(CFLAGSPROF) LDFLAGS += $(LDFLAGSPROF) @@ -312,7 +313,7 @@ $(OBJS) $(OBJASM) $(OBJSO) $(OBJCLI) $(OBJCHK) $(OBJCHK_8) $(OBJCHK_10) $(OBJEXA %.dll.o: %.rc x264.h $(RC) $(RCFLAGS)$@ -DDLL $< -%.o: %.rc x264.h +%.o: %.rc x264.h x264res.manifest $(RC) $(RCFLAGS)$@ $< .depend: config.mak diff --git a/common/osdep.h b/common/osdep.h index 75a991c5..2e35832b 100644 --- a/common/osdep.h +++ b/common/osdep.h @@ -116,30 +116,65 @@ static inline int x264_snprintf( char *s, size_t n, const char *fmt, ... ) #endif #ifdef _WIN32 -#define utf8_to_utf16( utf8, utf16 )\ - MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, utf8, -1, utf16, sizeof(utf16)/sizeof(wchar_t) ) - /* Functions for dealing with Unicode on Windows. */ -static inline FILE *x264_fopen( const char *filename, const char *mode ) +static inline wchar_t *x264_utf8_to_utf16( const char *utf8 ) { - wchar_t filename_utf16[MAX_PATH]; - wchar_t mode_utf16[16]; - if( utf8_to_utf16( filename, filename_utf16 ) && utf8_to_utf16( mode, mode_utf16 ) ) - return _wfopen( filename_utf16, mode_utf16 ); + int len = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, utf8, -1, NULL, 0 ); + if( len ) + { + wchar_t *utf16 = malloc( len * sizeof( wchar_t ) ); + if( utf16 ) + { + if( MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, utf8, -1, utf16, len ) ) + return utf16; + free( utf16 ); + } + } return NULL; } +static inline wchar_t *x264_utf8_to_utf16_try_buf( const char *utf8, wchar_t *buf_utf16, int buf_len ) { + if( MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, utf8, -1, buf_utf16, buf_len ) ) + return buf_utf16; + return x264_utf8_to_utf16( utf8 ); +} + +#define x264_fopen( filename, mode ) x264_fopen_internal( filename, L##mode ) +static inline FILE *x264_fopen_internal( const char *filename, const wchar_t *mode_utf16 ) +{ + FILE *f = NULL; + wchar_t filename_buf[MAX_PATH]; + wchar_t *filename_utf16 = x264_utf8_to_utf16_try_buf( filename, filename_buf, MAX_PATH ); + if( filename_utf16 ) + { + f = _wfopen( filename_utf16, mode_utf16 ); + if( filename_utf16 != filename_buf ) + free( filename_utf16 ); + } + return f; +} + static inline int x264_rename( const char *oldname, const char *newname ) { - wchar_t oldname_utf16[MAX_PATH]; - wchar_t newname_utf16[MAX_PATH]; - if( utf8_to_utf16( oldname, oldname_utf16 ) && utf8_to_utf16( newname, newname_utf16 ) ) + int ret = -1; + wchar_t oldname_buf[MAX_PATH]; + wchar_t *oldname_utf16 = x264_utf8_to_utf16_try_buf( oldname, oldname_buf, MAX_PATH ); + if( oldname_utf16 ) { - /* POSIX says that rename() removes the destination, but Win32 doesn't. */ - _wunlink( newname_utf16 ); - return _wrename( oldname_utf16, newname_utf16 ); + wchar_t newname_buf[MAX_PATH]; + wchar_t *newname_utf16 = x264_utf8_to_utf16_try_buf( newname, newname_buf, MAX_PATH ); + if( newname_utf16 ) + { + /* POSIX says that rename() removes the destination, but Win32 doesn't. */ + _wunlink( newname_utf16 ); + ret = _wrename( oldname_utf16, newname_utf16 ); + if( newname_utf16 != newname_buf ) + free( newname_utf16 ); + } + if( oldname_utf16 != oldname_buf ) + free( oldname_utf16 ); } - return -1; + return ret; } #define x264_struct_stat struct _stati64 @@ -147,10 +182,16 @@ static inline int x264_rename( const char *oldname, const char *newname ) static inline int x264_stat( const char *path, x264_struct_stat *buf ) { - wchar_t path_utf16[MAX_PATH]; - if( utf8_to_utf16( path, path_utf16 ) ) - return _wstati64( path_utf16, buf ); - return -1; + int ret = -1; + wchar_t path_buf[MAX_PATH]; + wchar_t *path_utf16 = x264_utf8_to_utf16_try_buf( path, path_buf, MAX_PATH ); + if( path_utf16 ) + { + ret = _wstati64( path_utf16, buf ); + if( path_utf16 != path_buf ) + free( path_utf16 ); + } + return ret; } #else #define x264_fopen fopen @@ -197,18 +238,43 @@ static inline int x264_vfprintf( FILE *stream, const char *format, va_list arg ) return vfprintf( stream, format, arg ); } -static inline int x264_is_pipe( const char *path ) +static inline int x264_is_regular_file_path( const char *path ) { - wchar_t path_utf16[MAX_PATH]; - if( utf8_to_utf16( path, path_utf16 ) ) - return WaitNamedPipeW( path_utf16, 0 ); - return 0; + int ret = -1; + wchar_t path_buf[MAX_PATH]; + wchar_t *path_utf16 = x264_utf8_to_utf16_try_buf( path, path_buf, MAX_PATH ); + if( path_utf16 ) + { + x264_struct_stat buf; + if( _wstati64( path_utf16, &buf ) ) + ret = !WaitNamedPipeW( path_utf16, 0 ); + else + ret = S_ISREG( buf.st_mode ); + if( path_utf16 != path_buf ) + free( path_utf16 ); + } + return ret; } #else #define x264_vfprintf vfprintf -#define x264_is_pipe(x) 0 + +static inline int x264_is_regular_file_path( const char *filename ) +{ + x264_struct_stat file_stat; + if( x264_stat( filename, &file_stat ) ) + return 1; + return S_ISREG( file_stat.st_mode ); +} #endif +static inline int x264_is_regular_file( FILE *filehandle ) +{ + x264_struct_stat file_stat; + if( x264_fstat( fileno( filehandle ), &file_stat ) ) + return 1; + return S_ISREG( file_stat.st_mode ); +} + #define x264_glue3_expand(x,y,z) x##_##y##_##z #define x264_glue3(x,y,z) x264_glue3_expand(x,y,z) @@ -510,20 +576,4 @@ static ALWAYS_INLINE void x264_prefetch( void *p ) #define x264_lower_thread_priority(p) #endif -static inline int x264_is_regular_file( FILE *filehandle ) -{ - x264_struct_stat file_stat; - if( x264_fstat( fileno( filehandle ), &file_stat ) ) - return 1; - return S_ISREG( file_stat.st_mode ); -} - -static inline int x264_is_regular_file_path( const char *filename ) -{ - x264_struct_stat file_stat; - if( x264_stat( filename, &file_stat ) ) - return !x264_is_pipe( filename ); - return S_ISREG( file_stat.st_mode ); -} - #endif /* X264_OSDEP_H */ diff --git a/input/avs.c b/input/avs.c index 1159536b..ed80821c 100644 --- a/input/avs.c +++ b/input/avs.c @@ -254,28 +254,48 @@ static float get_avs_version( avs_hnd_t *h ) } #ifdef _WIN32 -static int utf16_to_ansi( const wchar_t *utf16, char *ansi ) +static char *utf16_to_ansi( const wchar_t *utf16 ) { BOOL invalid; - return WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, utf16, -1, ansi, MAX_PATH, NULL, &invalid ) && !invalid; + int len = WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, utf16, -1, NULL, 0, NULL, &invalid ); + if( len && !invalid ) + { + char *ansi = malloc( len * sizeof( char ) ); + if( ansi ) + { + if( WideCharToMultiByte( CP_ACP, WC_NO_BEST_FIT_CHARS, utf16, -1, ansi, len, NULL, &invalid ) && !invalid ) + return ansi; + free( ansi ); + } + } + return NULL; } -static int utf8_to_ansi( const char *filename, char *ansi_filename ) +static char *utf8_to_ansi( const char *filename ) { - wchar_t filename_utf16[MAX_PATH]; - if( utf8_to_utf16( filename, filename_utf16 ) ) + char *ansi = NULL; + wchar_t *filename_utf16 = x264_utf8_to_utf16( filename ); + if( filename_utf16 ) { /* Check if the filename already is valid ANSI. */ - if( utf16_to_ansi( filename_utf16, ansi_filename ) ) - return 1; - - /* Check for a legacy 8.3 short filename. */ - int short_length = GetShortPathNameW( filename_utf16, filename_utf16, MAX_PATH ); - if( short_length > 0 && short_length < MAX_PATH ) - if( utf16_to_ansi( filename_utf16, ansi_filename ) ) - return 1; + if( !(ansi = utf16_to_ansi( filename_utf16 )) ) + { + /* Check for a legacy 8.3 short filename. */ + int len = GetShortPathNameW( filename_utf16, NULL, 0 ); + if( len ) + { + wchar_t *short_utf16 = malloc( len * sizeof( wchar_t ) ); + if( short_utf16 ) + { + if( GetShortPathNameW( filename_utf16, short_utf16, len ) ) + ansi = utf16_to_ansi( short_utf16 ); + free( short_utf16 ); + } + } + } + free( filename_utf16 ); } - return 0; + return ansi; } #endif @@ -305,8 +325,8 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c #ifdef _WIN32 /* Avisynth doesn't support Unicode filenames. */ - char ansi_filename[MAX_PATH]; - FAIL_IF_ERROR( !utf8_to_ansi( psz_filename, ansi_filename ), "invalid ansi filename\n" ); + char *ansi_filename = utf8_to_ansi( psz_filename ); + FAIL_IF_ERROR( !ansi_filename, "invalid ansi filename\n" ); AVS_Value arg = avs_new_value_string( ansi_filename ); #else AVS_Value arg = avs_new_value_string( psz_filename ); @@ -318,6 +338,9 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c if( !strcasecmp( filename_ext, "avs" ) ) { res = h->func.avs_invoke( h->env, "Import", arg, NULL ); +#ifdef _WIN32 + free( ansi_filename ); +#endif FAIL_IF_ERROR( avs_is_error( res ), "%s\n", avs_as_error( res ) ); /* check if the user is using a multi-threaded script and apply distributor if necessary. adapted from avisynth's vfw interface */ @@ -358,6 +381,9 @@ static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, c } x264_cli_printf( X264_LOG_INFO, "failed\n" ); } +#ifdef _WIN32 + free( ansi_filename ); +#endif FAIL_IF_ERROR( !filter[i], "unable to find source filter to open `%s'\n", psz_filename ); } FAIL_IF_ERROR( !avs_is_clip( res ), "`%s' didn't return a video clip\n", psz_filename ); diff --git a/x264.c b/x264.c index 38cd688f..54dcfc4e 100644 --- a/x264.c +++ b/x264.c @@ -80,7 +80,7 @@ static wchar_t org_console_title[CONSOLE_TITLE_SIZE] = L""; void x264_cli_set_console_title( const char *title ) { wchar_t title_utf16[CONSOLE_TITLE_SIZE]; - if( utf8_to_utf16( title, title_utf16 ) ) + if( MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, title, -1, title_utf16, CONSOLE_TITLE_SIZE ) ) SetConsoleTitleW( title_utf16 ); } diff --git a/x264res.manifest b/x264res.manifest new file mode 100644 index 00000000..93e93162 --- /dev/null +++ b/x264res.manifest @@ -0,0 +1,10 @@ + + + + + + true + UTF-8 + + + diff --git a/x264res.rc b/x264res.rc index cfac1f92..87d46949 100644 --- a/x264res.rc +++ b/x264res.rc @@ -35,6 +35,10 @@ #define str(s) #s #define xstr(s) str(s) +#ifndef DLL +1 RT_MANIFEST "x264res.manifest" +#endif + VS_VERSION_INFO VERSIONINFO FILEVERSION 0, X264_BUILD, X264_REV, X264_REV_DIFF PRODUCTVERSION 0, X264_BUILD, X264_REV, X264_REV_DIFF