diff --git a/tools/winebuild/build.h b/tools/winebuild/build.h index f99fa69d573..1d5ae469c49 100644 --- a/tools/winebuild/build.h +++ b/tools/winebuild/build.h @@ -205,6 +205,7 @@ extern int output( const char *format, ... ) extern const char *get_as_command(void); extern const char *get_ld_command(void); extern const char *get_nm_command(void); +extern const char *get_windres_command(void); extern char *get_temp_file_name( const char *prefix, const char *suffix ); extern void output_standard_file_header(void); extern FILE *open_input_file( const char *srcdir, const char *name ); @@ -250,6 +251,7 @@ extern void load_res16_file( const char *name, DLLSPEC *spec ); extern void output_res16_data( DLLSPEC *spec ); extern void output_res16_directory( DLLSPEC *spec ); extern void output_spec16_file( DLLSPEC *spec ); +extern void output_res_o_file( DLLSPEC *spec ); extern void BuildRelays16(void); extern void BuildRelays32(void); diff --git a/tools/winebuild/main.c b/tools/winebuild/main.c index 0a16afff25f..10746361481 100644 --- a/tools/winebuild/main.c +++ b/tools/winebuild/main.c @@ -96,7 +96,8 @@ enum exec_mode_values MODE_EXE, MODE_DEF, MODE_RELAY16, - MODE_RELAY32 + MODE_RELAY32, + MODE_RESOURCES }; static enum exec_mode_values exec_mode = MODE_NONE; @@ -243,6 +244,7 @@ static const char usage_str[] = " --exe Build a .c file for an executable\n" " --relay16 Build the 16-bit relay assembly routines\n" " --relay32 Build the 32-bit relay assembly routines\n\n" +" --resources Build a .o file for the resource files\n\n" "The mode options are mutually exclusive; you must specify one and only one.\n\n"; enum long_options_values @@ -257,6 +259,7 @@ enum long_options_values LONG_OPT_NXCOMPAT, LONG_OPT_RELAY16, LONG_OPT_RELAY32, + LONG_OPT_RESOURCES, LONG_OPT_SAVE_TEMPS, LONG_OPT_SUBSYSTEM, LONG_OPT_VERSION @@ -276,6 +279,7 @@ static const struct option long_options[] = { "nxcompat", 1, 0, LONG_OPT_NXCOMPAT }, { "relay16", 0, 0, LONG_OPT_RELAY16 }, { "relay32", 0, 0, LONG_OPT_RELAY32 }, + { "resources", 0, 0, LONG_OPT_RESOURCES }, { "save-temps", 0, 0, LONG_OPT_SAVE_TEMPS }, { "subsystem", 1, 0, LONG_OPT_SUBSYSTEM }, { "version", 0, 0, LONG_OPT_VERSION }, @@ -463,6 +467,9 @@ static char **parse_options( int argc, char **argv, DLLSPEC *spec ) case LONG_OPT_RELAY32: set_exec_mode( MODE_RELAY32 ); break; + case LONG_OPT_RESOURCES: + set_exec_mode( MODE_RESOURCES ); + break; case LONG_OPT_SAVE_TEMPS: save_temps = 1; break; @@ -621,6 +628,10 @@ int main(int argc, char **argv) if (argv[0]) fatal_error( "file argument '%s' not allowed in this mode\n", argv[0] ); BuildRelays32(); break; + case MODE_RESOURCES: + load_resources( argv, spec ); + output_res_o_file( spec ); + break; default: usage(1); break; diff --git a/tools/winebuild/res32.c b/tools/winebuild/res32.c index 739b322b695..5eabc1b7ce4 100644 --- a/tools/winebuild/res32.c +++ b/tools/winebuild/res32.c @@ -21,6 +21,7 @@ #include "config.h" #include "wine/port.h" +#include #include #include #include @@ -55,6 +56,7 @@ struct resource struct string_id name; const void *data; unsigned int data_size; + unsigned short mem_options; unsigned short lang; }; @@ -89,6 +91,9 @@ static const unsigned char *file_pos; /* current position in resource file */ static const unsigned char *file_end; /* end of resource file */ static const char *file_name; /* current resource file name */ +static unsigned char *file_out_pos; /* current position in output resource file */ +static unsigned char *file_out_end; /* end of output buffer */ + /* size of a resource directory with n entries */ #define RESOURCE_DIR_SIZE (4 * sizeof(unsigned int)) #define RESOURCE_DIR_ENTRY_SIZE (2 * sizeof(unsigned int)) @@ -178,6 +183,41 @@ static void get_string( struct string_id *str ) } } +/* put a word into the resource file */ +static void put_word( unsigned short val ) +{ + if (byte_swapped) val = (val << 8) | (val >> 8); + *(unsigned short *)file_out_pos = val; + file_out_pos += sizeof(unsigned short); + assert( file_out_pos <= file_out_end ); +} + +/* put a dword into the resource file */ +static void put_dword( unsigned int val ) +{ + if (byte_swapped) + val = ((val << 24) | ((val << 8) & 0x00ff0000) | ((val >> 8) & 0x0000ff00) | (val >> 24)); + *(unsigned int *)file_out_pos = val; + file_out_pos += sizeof(unsigned int); + assert( file_out_pos <= file_out_end ); +} + +/* put a string into the resource file */ +static void put_string( const struct string_id *str ) +{ + if (str->str) + { + const WCHAR *p = str->str; + while (*p) put_word( *p++ ); + put_word( 0 ); + } + else + { + put_word( 0xffff ); + put_word( str->id ); + } +} + /* check the file header */ /* all values must be zero except header size */ static int check_header(void) @@ -204,7 +244,7 @@ static void load_next_resource( DLLSPEC *spec ) unsigned int hdr_size; struct resource *res = add_resource( spec ); - res->data_size = (get_dword() + 3) & ~3; + res->data_size = get_dword(); hdr_size = get_dword(); if (hdr_size & 3) fatal_error( "%s header size not aligned\n", file_name ); @@ -213,12 +253,12 @@ static void load_next_resource( DLLSPEC *spec ) get_string( &res->name ); if ((unsigned long)file_pos & 2) get_word(); /* align to dword boundary */ get_dword(); /* skip data version */ - get_word(); /* skip mem options */ + res->mem_options = get_word(); res->lang = get_word(); get_dword(); /* skip version */ get_dword(); /* skip characteristics */ - file_pos = (const unsigned char *)res->data + res->data_size; + file_pos = (const unsigned char *)res->data + ((res->data_size + 3) & ~3); if (file_pos > file_end) fatal_error( "%s is a truncated file\n", file_name ); } @@ -441,7 +481,7 @@ void output_resources( DLLSPEC *spec ) for (i = 0, res = spec->resources; i < spec->nb_resources; i++, res++) output( "\t.long .L__wine_spec_res_%d-.L__wine_spec_rva_base,%u,0,0\n", - i, res->data_size ); + i, (res->data_size + 3) & ~3 ); /* dump the name strings */ @@ -458,10 +498,98 @@ void output_resources( DLLSPEC *spec ) { output( "\n\t.align %d\n", get_alignment(get_ptr_size()) ); output( ".L__wine_spec_res_%d:\n", i ); - dump_bytes( res->data, res->data_size ); + dump_bytes( res->data, (res->data_size + 3) & ~3 ); } output( ".L__wine_spec_resources_end:\n" ); output( "\t.byte 0\n" ); free_resource_tree( tree ); } + +static unsigned int get_resource_header_size( const struct resource *res ) +{ + unsigned int size = 5 * sizeof(unsigned int) + 2 * sizeof(unsigned short); + + if (!res->type.str) size += 2 * sizeof(unsigned short); + else size += (strlenW(res->type.str) + 1) * sizeof(WCHAR); + + if (!res->name.str) size += 2 * sizeof(unsigned short); + else size += (strlenW(res->name.str) + 1) * sizeof(WCHAR); + + return size; +} + +/* output the resources into a .o file */ +void output_res_o_file( DLLSPEC *spec ) +{ + unsigned int i, total_size; + unsigned char *data; + char *res_file, *cmd; + const char *prog; + int fd, err; + + if (!spec->nb_resources) fatal_error( "--resources mode needs at least one resource file as input\n" ); + if (!output_file_name) fatal_error( "No output file name specified\n" ); + + total_size = 32; /* header */ + + for (i = 0; i < spec->nb_resources; i++) + { + total_size += (get_resource_header_size( &spec->resources[i] ) + 3) & ~3; + total_size += (spec->resources[i].data_size + 3) & ~3; + } + data = xmalloc( total_size ); + + byte_swapped = 0; + file_out_pos = data; + file_out_end = data + total_size; + + put_dword( 0 ); /* ResSize */ + put_dword( 32 ); /* HeaderSize */ + put_word( 0xffff ); /* ResType */ + put_word( 0x0000 ); + put_word( 0xffff ); /* ResName */ + put_word( 0x0000 ); + put_dword( 0 ); /* DataVersion */ + put_word( 0 ); /* Memory options */ + put_word( 0 ); /* Language */ + put_dword( 0 ); /* Version */ + put_dword( 0 ); /* Characteristics */ + + for (i = 0; i < spec->nb_resources; i++) + { + unsigned int header_size = get_resource_header_size( &spec->resources[i] ); + + put_dword( spec->resources[i].data_size ); + put_dword( header_size ); + put_string( &spec->resources[i].type ); + put_string( &spec->resources[i].name ); + if ((unsigned long)file_out_pos & 2) put_word( 0 ); + put_dword( 0 ); + put_word( spec->resources[i].mem_options ); + put_word( spec->resources[i].lang ); + put_dword( 0 ); + put_dword( 0 ); + memcpy( file_out_pos, spec->resources[i].data, spec->resources[i].data_size ); + file_out_pos += spec->resources[i].data_size; + while ((unsigned long)file_out_pos & 3) *file_out_pos++ = 0; + } + assert( file_out_pos == file_out_end ); + + res_file = get_temp_file_name( output_file_name, ".res" ); + if ((fd = open( res_file, O_WRONLY|O_CREAT|O_TRUNC, 0600 )) == -1) + fatal_error( "Cannot create %s\n", res_file ); + if (write( fd, data, total_size ) != total_size) + fatal_error( "Error writing to %s\n", res_file ); + close( fd ); + free( data ); + + prog = get_windres_command(); + cmd = xmalloc( strlen(prog) + strlen(res_file) + strlen(output_file_name) + 9 ); + sprintf( cmd, "%s -i %s -o %s", prog, res_file, output_file_name ); + if (verbose) fprintf( stderr, "%s\n", cmd ); + err = system( cmd ); + if (err) fatal_error( "%s failed with status %d\n", prog, err ); + free( cmd ); + output_file_name = NULL; /* so we don't try to assemble it */ +} diff --git a/tools/winebuild/utils.c b/tools/winebuild/utils.c index d6eab0e22fc..8d31fff7125 100644 --- a/tools/winebuild/utils.c +++ b/tools/winebuild/utils.c @@ -319,6 +319,27 @@ const char *get_nm_command(void) return nm_command; } +const char *get_windres_command(void) +{ + static char *windres_command; + + if (!windres_command) + { + if (target_alias) + { + windres_command = xmalloc( strlen(target_alias) + sizeof("-windres") ); + strcpy( windres_command, target_alias ); + strcat( windres_command, "-windres" ); + } + else + { + static const char * const commands[] = { "windres", NULL }; + if (!(windres_command = find_tool( commands ))) windres_command = xstrdup("windres"); + } + } + return windres_command; +} + /* get a name for a temp file, automatically cleaned up on exit */ char *get_temp_file_name( const char *prefix, const char *suffix ) { diff --git a/tools/winebuild/winebuild.man.in b/tools/winebuild/winebuild.man.in index da0728e98eb..855bf4ad076 100644 --- a/tools/winebuild/winebuild.man.in +++ b/tools/winebuild/winebuild.man.in @@ -53,6 +53,13 @@ Wine internal usage only, you should never need to use this option. .B \--relay32 Generate the assembly code for the 32-bit relay routines. This is for Wine internal usage only, you should never need to use this option. +.TP +.B \--resources +Generate a .o file containing all the input resources. This is useful +when building with a PE compiler, since the PE binutils cannot handle +multiple resource files as input. For a standard Unix build, the +resource files are automatically included when building the spec file, +so there's no need for an intermediate .o file. .SH OPTIONS .TP .BI \--as-cmd= as-command