Release 980601 Sun May 31 13:40:13 1998 Alexandre Julliard <julliard@lrc.epfl.ch> * [if1632/signal.c] Added display of exception name. * [loader/task.c] Yet another attempt at fixing SwitchStackTo/SwitchStackBack. * [memory/selector.c] [relay32/builtin32.c] [tools/build.c] [win32/kernel32.c] Generate an assembly stub for Win32 register functions to make their names available at link time. * [programs/*/Makefile.in] Added hacks to support old resource compiler. Fri May 29 16:27:14 1998 Marcus Meissner <marcus@jet.franken.de> * [tools/testrun] Merge of my testscripts at home into one single perl program (tested with perl5). Works only on Linux due to 'ps' and 'ipcs' magic. * [controls/menu.c] Added some DefaultMenuItem stubs. * [debugger/stabs.c] Fixed stabs loading, now supports (int,int) typeinfo format used by gcc-2.8 and egcs-1. If it still crashes, please mail me. * [if1632/][relay32/] Added msvideo.dll (stubs only) Replaced some ptr by str for stringcases Added some new stubs (VxDCall, FindCloseNotif....) * [misc/network.c] Some argument fixes. * [misc/registry.c][misc/cpu.c] Registry initialization partially rewritten and enhanced. * [scheduler/*.c] Some additions so we don't do kill(0,SIGUSR1) (kill processgroup instead of targeted thread) Added SetThreadContext. Thu May 28 23:59:59 1998 Bertho Stultiens <bertho@akhphd.au.dk> * [tools/wrc/*] New resource compiler version 1.0.0 (28-May-1998) * [Make.rules.in] [Makefile.in] Changed and added rc rules to point to tools/wrc/wrc. * [configure.in] [include/config.h.in] Added check for function 'stricmp'. * [include/resource.h] Commented out the old resource structure to catch references. It also includes wrc_rsc.h. * [include/wrc_rsc.h] New file. Definitions for the resources generated with wrc. * [include/windows.h] Added #ifdef RC_INVOKED to exclude stdarg.h. Added SS_NOTIFY flag. * [include/winnls.h] Added SUBLANG_* definitions and corrected some defaults. * [loader/libres.c] Changed the sysres load functions to support wrc generated resources. * [resource/sysres_*.rc] Added #include <windows.h> * [resource/sysres.c] Changed declarations to match wrc's output * [resource/Makefile.in] Changed rules to work with wrc. * [tools/makedep.c] Changed generation of .rc file dependencies to .s target. Thu May 28 22:28:39 1998 Eric Kohl <ekohl@abo.rhein-zeitung.de> * [files/file.c][include/windows.c][relay32/kernel32.spec] Implemented GetFileAttributesEx32A/W. * [misc/imagelist.h][include/commctrl.h][relay32/comctl32.spec] Added ImageList_Read and ImageList_Write stubs. Added ImageList_AddIcon function. Added ImageList_LoadImage. It is the same as ImageList_LoadImage32A. * [controls/header.c] Fixed bitmap drawing bug. Added full bitmap support. * [include/commctrl.h] Added missing header macros. * [controls/toolbar.c][include/toolbar.h][include/commctrl.h] [controls/commctrl.c] [relay32/comctl32.spec] First implementation of toolbar control. Implemented CreateToolbar, CreateToolbarEx and CreateMappedBitmap. * [controls/progress.c][controls/status.c] Some code cleanup. * [controls/commctrl.c][include/commctrl.h][relay32/comctl32.spec] Removed CreateStatusWindow16 and DrawStatusText16. CreateStatusWindow is the same as CreateStatusWindow32A. DrawStatusText is the same as DrawStatusText32A. Thu May 28 16:01:28 1998 Matthew J. Francis <asbel@dial.pipex.com> * [objects/bitmap.c] [objects/bitmap.h] [objects/oembitmap.c] [objects/dc.c] [graphics/x11drv/bitblt.c] Added partial implementation of CreateDIBSection, with great thanks to Ulrich Weigand <weigand@informatik.uni-erlangen.de> for contributing the bulk of the patch. Wed May 27 19:04:31 1998 Ulrich Weigand <weigand@informatik.uni-erlangen.de> * [win32/kernel32.c] [if1632/thunk.c] [include/flatthunk.h] ThunkConnect16 and related functions moved to emulator. * [loader/ne/segment.c] Call DllEntryPoint with correct arguments. * [relay32/builtin32.c] Bugfix: Relay debugging did not work for multiple processes. * [controls/menu.c] Bugfix: dwItemData was not set for MF_OWNERDRAW menus. * [if1632/relay.c] [relay32/relay386.c] Relay messages converted to use DPRINTF. * [controls/desktop.c] [relay32/user32.spec] Implemented PaintDesktop. * [files/profile.c] [if1632/kernel.spec] [misc/network.c] [misc/printdrv.c] [relay32/winspool.spec] [win32/ordinals.c] [relay32/kernel32.spec] Some stubs added. * [relay32/mpr.spec] All ordinals were off by one. Tue May 26 13:32:57 1998 Bill Hawes <whawes@star.net> * [misc/lstr.c] [include/casemap.h] [tools/unimap.pl] Added Unicode case conversion routines towupper/towlower, with mapping tables casemap.h created by tools/unimap.pl. * [misc/ntdll.c] [include/winnls.h] [relay32/ntdll.spec] [relay32/advapi.spec] Minimal implementation of IsTextUnicode, just enough to get NT4 notepad to open ascii/unicode files. * [Make.rules.in] [resources/sysres_En.rc] Added include file dlgs.h for building resource files, so that resources can refer to defined values (e.g. pshHelp). * [misc/crtdll.c] [relay32/crtdll.spec] Use towupper/towlower for 32W case conversions. * [memory/string.c] Use towupper for 32W case conversions. * [ole/ole2nls.c] Use towupper for 32W case conversions; fix mem leak; minor cleanup * [controls/edit.c] Added soft break flag to edit state. Print unknown action values for WM_VSCROLL (action 190 occurs when running NT4 notepad.) Mon May 25 22:42:40 1998 Uwe Bonnes <bon@elektron.ikp.physik.tu-darmstadt.de> * [files/file.c] Care for a pathological case in SetFilePointer. * [graphics/x11drv/xfont.c] Handle longer Font names in LFD_ComposeLFD and try to catch errors. * [loader/pe_image.c] Unload Dummymodule when PE_LoadLibraryEx32A fails with PE_LoadImage (makes Encarta 98 installer proceed). * [misc/registry.c] Move a check for a special case in RegCreateKeyEx32W after the check for existence. Tue May 25 20:18:26 1998 Matthew Becker <mbecker@glasscity.net> * [misc/ntdll.c] Added some stubs, just guessing at the size of their param lists. * [misc/registry.c] Added stubs for RegUnLoadKey, RegSetKeySecurity, RegSaveKey, RegRestoreKey, and RegReplaceKey * [programs/regtest/regtest.c] Updated registry testing program. Sun May 24 18:11:40 1998 Alex Priem <alexp@sci.kun.nl> * [file/profile.c] Added flag 'return_values' to PROFILE_GetSection. Sun May 24 13:41:10 1998 James Juran <jrj120@psu.edu> * [misc/shell.c] [files/directory.c] Documentation/debugging info additions. * [*/*.c] [include/*.h] Moved many extern function definitions to appropriate header files. Cleaned up a few compile warnings. If #include "debug.h" is present, removed #include <stdio.h>. debug.h includes stdio.h, so it is not necessary to include both. * [graphics/*.c] [if1632/signal.c] [ipc/*.c] [scheduler/*.c] [win32/*.c] [windows/*.c] Final patch to convert fprintf statements to new debugging interface. Some fprintfs are still left, especially in the debugger/ directory. However, IMHO, it's not worth the effort to change the rest. Fri May 22 21:58:35 1998 Morten Welinder <terra@diku.dk> * [windows/keyboard.c] Correct handling of keys "`-=[]\;',./". Fri May 22 12:06:00 1998 Per Lindström <pelinstr@algonet.se> * [include/windows.h] [relay32/kernel32.spec] [win32/console.c] Added stub for ReadConsoleOutputCharacter32A. Thu May 21 16:45:48 1998 Pascal Cuoq <pcuoq@ens-lyon.fr> * [ole/ole2nls.c] Began better implementation of LCMapString32A. Not very well tested yet, and still need improvements. * [controls/scroll.c] Documented functions. Wed May 20 21:37:56 1998 Peter Hunnisett <hunnise@nortel.ca> * [include/windows.h][misc/main.c] Change SystemParameterInfo to support SPI_GETHIGHCONTRAST. Also include some missing SPI_ definitions. * [include/dsound.h][multimedia/dsound.c][relay32/dplayx.spec] Added stubs for DirectPlayLobbyCreate[AW]. Not sure if these should go into a new files dplayx.c? Anyone care? * [include/winnls.h] Added two missing flags for the CompareString32 functions.
diff --git a/tools/build.c b/tools/build.c index 319b6e7..bc36294 100644 --- a/tools/build.c +++ b/tools/build.c
@@ -971,7 +971,7 @@ static int BuildSpec32File( char * specfile, FILE *outfile ) { ORDDEF *odp; - int i, nb_names, nb_reg_funcs = 0; + int i, nb_names; fprintf( outfile, "/* File generated automatically from %s; do not edit! */\n\n", specfile ); @@ -988,6 +988,25 @@ i, DLLName, i ); } + /* Output code for all register functions */ + + fprintf( outfile, "#ifdef __i386__\n" ); + for (i = Base, odp = OrdinalDefinitions + Base; i <= Limit; i++, odp++) + { + if (odp->type != TYPE_REGISTER) continue; + fprintf( outfile, + "__asm__(\".align 4\\n\\t\"\n" + " \".globl " PREFIX "%s\\n\\t\"\n" + " \".type " PREFIX "%s,@function\\n\\t\"\n" + " \"%s:\\n\\t\"\n" + " \"pushl $" PREFIX "__regs_%s\\n\\t\"\n" + " \"pushl $" PREFIX "CALL32_Regs\\n\\t\"\n" + " \"ret\");\n", + odp->u.func.link_name, odp->u.func.link_name, + odp->u.func.link_name, odp->u.func.link_name ); + } + fprintf( outfile, "#endif\n" ); + /* Output the DLL functions prototypes */ for (i = Base, odp = OrdinalDefinitions + Base; i <= Limit; i++, odp++) @@ -1106,7 +1125,6 @@ break; case TYPE_REGISTER: args = 0xfe; - nb_reg_funcs++; break; default: args = 0xff; @@ -1125,7 +1143,6 @@ fprintf( outfile, " %d,\n", Base ); fprintf( outfile, " %d,\n", Limit - Base + 1 ); fprintf( outfile, " %d,\n", nb_names ); - fprintf( outfile, " %d,\n", nb_reg_funcs ); fprintf( outfile, " Functions,\n" " FuncNames,\n"
diff --git a/tools/makedep.c b/tools/makedep.c index 11f77d3..c45eb0c 100644 --- a/tools/makedep.c +++ b/tools/makedep.c
@@ -279,7 +279,7 @@ else if (!strcmp( ext, ".rc" )) /* resource file */ { *ext = '\0'; - fprintf( file, "%s.c %s.h: %s", obj, obj, pFile->filename ); + fprintf( file, "%s.s: %s", obj, pFile->filename ); *column += 2 * strlen(obj) + strlen(pFile->filename) + 7; } else
diff --git a/tools/testrun b/tools/testrun new file mode 100755 index 0000000..499e134 --- /dev/null +++ b/tools/testrun
@@ -0,0 +1,274 @@ +#!/usr/bin/perl +# Copyright 1996-1998 Marcus Meissner +# IPC remove code Copyright 1995 Michael Veksler +# +# This perl script automatically test runs ALL windows .exe and .scr binaries +# it finds (and can access) on your computer. It creates a subdirectory called +# runs/ and stores the output there. It also does (unique) diffs between runs. +# +# It only reruns the test if ChangeLog or the executeable is NEWER than the +# run file. (If you want to rerun everything inbetween releases, touch +# ChangeLog.) + +# +# BEGIN OF USER CONFIGURATION +# +# Path to WINE executeable. If not specified, 'wine' is searched in the path. +# +$wine = 'wine'; +# +# WINE options. -managed when using a windowmanager is probably not good in +# automatic testruns. +# +$wineoptions=''; +# +# Path to WINE ChangeLog. Used as timestamp for new releases... +# +$changelog = '/home/marcus/wine/ChangeLog'; +# +# How long before automatically killing all subprocesses +# 30 is good for automatic testing, 300 or more for interactive testing. +# +$waittime = 30; +# +#diff command +# +$diff='diff -u'; +# +# truncate at how-much-lines +# +$trunclines=200; +# +$<||die "Running this script under UID 0 is a great security risk (and risk for existing windows installations on mounted DOS/W95 partitions). If you really want to, comment out this line.\n"; +# +# END OF USER CONFIGURATION +# + +if (! -d "runs") { die "no subdirectory runs/ found in $cwd. Please create one first!\n";} + +# look for the exact path to wine executeable in case we need it for a +# replacement changelog. +if (! ($wine =~ /\//)) { # no path specified. Look it up. + @paths = split(/:/,$ENV{'PATH'}); + foreach $path (@paths) { + if (-e "$path/$wine" && -x "$path/$wine") { + $wine = "$path/$wine"; + last; + } + } +} + +# if we don't have a changelog use the modification date of the WINE executeable +if (! -e $changelog) { + $changelog = $wine; +} + +# sanity check so we just fill runs/ with errors. +(-x $wine) || die "no $wine executable found!\n"; +# dito. will print usage +system("$wine -h >/dev/null")||die "wine call failed:$!\n"; + +print "Using $wine as WINE executeable.\n"; +print "Using $changelog as testrun timereference.\n"; + +chomp($cwd = `pwd`); + +# Find out all present semaphores so we don't remove them later. +$IPC_RMID=0; +$USER=$ENV{'USER'}; +open(IPCS,"ipcs|"); +while(<IPCS>) { + split; + # try to find out the IPC-ID, assume it is the first number. + foreach (@_) { + $_ ne int($_) && next; # not a decimal number + $num=$_; + last; + } + if (/sem/i .. /^\s*$/ ) { + index($_,$USER)>=0 || next; + $sem_used{$num}=1; + print "found $num\n"; + } +} +close(IPCS); + +sub kill_subprocesses { + local($killedalready,%parentof,%kids,$changed,%cmdline); + + # FIXME: Linux ps dependend... + # + open(PSAUX,"ps --format pid,ppid,comm|"); + # lookup all processes, remember their parents and cmdlines. + %parentof=(); + while (<PSAUX>) { + if (/\s*(\d*)\s*(\d*)\s*(\S*)/) { + $parentof{$1}=$2; + $cmdline{$1}=$3; + } + } + close(PSAUX); + + # find out all kids of this perlscript + %kids = (); + $kids{$$} = 1; + $changed = 1; + while ($changed) { + $changed = 0; + foreach (keys %parentof) { + next if ($kids{$_}); + if ($kids{$parentof{$_}}) { + $changed = 1; + $kids{$_}=1; + } + } + } + # .. but do not consider us for killing + delete $kids{$$}; + # remove all processes killed in the meantime from %killedalready. + foreach $pid (keys %killedalready) { + delete $killedalready{$pid} if (!$kids{$pid} ); + } + # kill all subprocesses called 'wine'. Do not kill find, diff, sh + # and friends, which are also subprocesses of us. + foreach (keys %kids) { + next unless ($cmdline{$_} =~ /wine/); + # if we have already killed it using -TERM, use -KILL + if ($killedalready{$_}) { + kill(9,$_); # FIXME: use correct number? + } else { + kill(15,$_); # FIXME: use correct number? + } + $killedalready{$_}=1; + } + alarm($waittime); # wait again... +}; + +# borrowed from tools/ipcl. See comments there. +# killing wine subprocesses unluckily leaves all of their IPC stuff lying +# around. We have to wipe it or we run out of it. +sub cleanup_wine_ipc { + open(IPCS,"ipcs|"); + while(<IPCS>) { + split; + # try to find out the IPC-ID, assume it is the first number. + foreach (@_) { + $_ ne int($_) && next; # not a decimal number + $num=$_; + last; + } + # was there before start of this script, skip it. + # + # FIXME: this doesn't work for programs started during the testrun. + # + if (/sem/i .. /^\s*$/ ) { + index($_,$USER)>=0 || next; + push(@sem,$num); + } + } + foreach (@sem) { + $sem_used{$_} && next; + semctl($_, 0, $IPC_RMID,0); + } + close(IPCS); +} + +# kill all subwineprocesses for automatic runs. +sub alarmhandler { + print "timer triggered.\n"; + &kill_subprocesses; +} + +$SIG{'ALRM'} = "alarmhandler"; + +# NOTE: following find will also cross NFS mounts, so be sure to have nothing +# mounted that's not on campus or add relevant ! -fstype nfs or similar. +# + +open(FIND,"find / -type f \\( -name \"*.EXE\" -o -name \"*.exe\" -o -name \"*.scr\" -o -name \"*.SCR\" \\) -print|"); +while ($exe=<FIND>) { + chop($exe); + + # This could change during a testrun (by doing 'make' for instance) + # FIXME: doesn't handle missing libwine.so during compile... + (-x $wine) || die "no $wine executable found!\n"; + + # Skip all mssetup, acmsetup , installshield whatever exes. + # they seem to work, mostly and starting them is just annoying. + next if ($exe =~ /acmsetup/); + next if ($exe =~ /unwise/); + next if ($exe =~ /testexit/); + next if ($exe =~ /_msset/); + next if ($exe =~ /isun/i); + next if ($exe =~ /st4u/); + next if ($exe =~ /st5u/); + next if ($exe =~ /_mstest/); + next if ($exe =~ /_isdel/); + next if ($exe =~ /ms-setup/); + next if ($exe =~ /~mss/); + next if ($exe =~ /unin/); + + $runfile = $exe; + $runfile =~ s/[\/ ]/_/g; + $runfile =~ s/\.exe$//g; + $runfile =~ s/\.scr$//ig; + $flag=0; + # + # Check if changelog is newer, if not, continue + # + if ( -e "runs/${runfile}.out" && + (-M $changelog > -M "runs/${runfile}.out") && + (-M $exe > -M "runs/${runfile}.out") + ) { + #print "skipping $exe, already done.\n"; + next; + } + + # check runfile if the exe is a DOS executeable or + # of different architecture (win32) + if (open(RUNFILE,"runs/$runfile.out")) { + while ($xrun=<RUNFILE>) { + chop($xrun); + if ($xrun=~ /LoadModule:.*error=11$/) { + $flag=1; + last; + } + if ($xrun=~ /LoadModule:.*error=21$/) { + $flag=2; + last; + } + } + close(RUNFILE); + if ($flag==1) { + #print "skipping $exe, seems to be a DOS executable.\n"; + next; + } + if ($flag==2) { + #print "skipping $exe, seems to be a non i386 executable.\n"; + next; + } + } + # now testrun... + print "$exe:\n"; + $dir = $exe; + $dir =~ s/^(.*)\/[^\/]*$/$1/; #cut of the basename. + + alarm($waittime); + + chdir($dir)||die "$dir:$!"; + if ($exe =~ /\.scr/i) { + system("echo quit|$wine $wineoptions \"$exe /s\" >$cwd/${runfile}.out 2>&1"); + } else { + system("echo quit|$wine $wineoptions \"$exe\" >$cwd/${runfile}.out 2>&1"); + } + alarm(1000);# so it doesn't trigger in the diff, kill or find. + + system("touch $cwd/runs/${runfile}.out"); + system("$diff $cwd/runs/${runfile}.out $cwd/${runfile}.out"); + system("head -$trunclines $cwd/${runfile}.out >$cwd/runs/${runfile}.out"); + unlink("$cwd/${runfile}.out"); + &kill_subprocesses; + &cleanup_wine_ipc; + chdir($cwd); +} +close(FIND);
diff --git a/tools/unimap.pl b/tools/unimap.pl new file mode 100644 index 0000000..0b3282f --- /dev/null +++ b/tools/unimap.pl
@@ -0,0 +1,121 @@ +#!/usr/bin/perl +# +# Reads the Unicode 2.0 "unidata2.txt" file and selects encodings +# for case pairs, then builds an include file "casemap.h" for the +# case conversion routines. + +use integer + +$INFILE = "unidata2.txt"; +$OUT = ">casemap.h"; +$TEST = ">allcodes"; + +# Open the data file ... +open INFILE or die "Can't open input file $INFILE!\n"; +open OUT or die "Can't open output file $OUT!\n"; +open TEST or die "Can't open output file $OUT!\n"; + +#Initialize the upper and lower hashes +%lwrtable = (); +%uprtable = (); +@low = ("0") x 256; +@upr = ("0") x 256; + +while ($line = <INFILE> ) +{ + # Decode the fields ... + ($code, $name, $cat, $comb, $bidi, + $decomp, $dec, $dig, $num, $mirror, + $oldname, $comment, $upper, $lower, $title) = split /;/, $line; + + #Get the high byte of the code + $high = substr $code, 0, 2; + if ($lower ne "") { + $low[hex $high] = "lblk" . $high; + $lwrtable{$code} = $lower; + } + if ($upper ne "") { + $upr[hex $high] = "ublk" . $high; + $uprtable{$code} = $upper; + } + #Write everything to the test file + printf TEST "%s %s %s\n", $code, + $upper ne "" ? $upper : "0000", + $lower ne "" ? $lower : "0000"; + +} +close(FILE); +close TEST; + +#Generate the header file +print OUT "/*\n"; +print OUT " * Automatically generated file -- do not edit!\n"; +print OUT " * (Use tools/unimap.pl for generation)\n"; +print OUT " *\n"; +print OUT " * Mapping tables for Unicode case conversion\n"; +print OUT " */\n\n"; + +#Write out the non-trivial mappings +for ($high = 0; $high < 256; $high++) { + #Check whether the table is needed + if (length $low[$high] < 6) { + next; + } + printf OUT "/* Lowercase mappings %02X00 - %02XFF */\n", + $high, $high; + printf OUT "static const WCHAR lblk%02X[256] = {\n", $high; + for ($low = 0; $low < 256; $low += 8) { + @patch = (); + for ($i = 0; $i < 8; $i++) { + $code = sprintf "%02X%02X", $high, $low + $i; + $map = $lwrtable{$code}; + if ($map eq "") { + $map = $code; + } + $patch[$i] = "0x" . $map; + } + printf OUT "\t%s, %s, %s, %s, %s, %s, %s, %s,\n", + @patch; + } + print OUT "};\n\n"; +} +print OUT "static const WCHAR * const lwrtable[256] = {\n"; +for ($i = 0; $i < 256; $i += 8) { + @patch = @low[$i+0 .. $i+7]; + printf OUT "\t%06s, %06s, %06s, %06s, %06s, %06s, %06s, %06s,\n", + @patch; +} +print OUT "};\n\n"; + +for ($high = 0; $high < 256; $high++) { + #Check whether the table is needed + if (length $upr[$high] < 6) { + next; + } + printf OUT "/* Uppercase mappings %02X00 - %02XFF */\n", + $high, $high; + printf OUT "static const WCHAR ublk%02X[256] = {\n", $high; + for ($low = 0; $low < 256; $low += 8) { + @patch = (); + for ($i = 0; $i < 8; $i++) { + $code = sprintf "%02X%02X", $high, $low + $i; + $map = $uprtable{$code}; + if ($map eq "") { + $map = $code; + } + $patch[$i] = "0x" . $map; + } + printf OUT "\t%s, %s, %s, %s, %s, %s, %s, %s,\n", + @patch; + } + print OUT "};\n\n"; +} +print OUT "static const WCHAR * const uprtable[256] = {\n"; +for ($i = 0; $i < 256; $i += 8) { + @patch = @upr[$i+0 .. $i+7]; + printf OUT "\t%06s, %06s, %06s, %06s, %06s, %06s, %06s, %06s,\n", + @patch; +} +print OUT "};\n\n"; + +close(OUT);
diff --git a/tools/wrc/Makefile.in b/tools/wrc/Makefile.in new file mode 100644 index 0000000..85eae63 --- /dev/null +++ b/tools/wrc/Makefile.in
@@ -0,0 +1,41 @@ +DEFS = -D__WINE__ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ + +PROGRAMS = wrc@PROGEXT@ +MODULE = none + +C_SRCS = \ + dumpres.c \ + genres.c \ + newstruc.c \ + preproc.c \ + readres.c \ + utils.c \ + wrc.c \ + writeres.c + +EXTRA_SRCS = parser.y parser.l +EXTRA_OBJS = y.tab.o lex.yy.o + +all: $(PROGRAMS) + +depend:: y.tab.h + +@MAKE_RULES@ + +wrc@PROGEXT@: $(OBJS) + $(CC) $(CFLAGS) -o wrc@PROGEXT@ $(OBJS) $(LEXLIB) + +y.tab.c y.tab.h: parser.y + $(YACC) -d -t $(SRCDIR)/parser.y + +lex.yy.c: parser.l + $(LEX) -8 $(SRCDIR)/parser.l + +clean:: + $(RM) y.tab.c y.tab.h lex.yy.c + +### Dependencies:
diff --git a/tools/wrc/README.wrc b/tools/wrc/README.wrc new file mode 100644 index 0000000..7c2b47a --- /dev/null +++ b/tools/wrc/README.wrc
@@ -0,0 +1,258 @@ +This is the first release (1.0.0 (28-May-1998)) of the new resource compiler +'wrc'. It improves the winerc implementation by these features: + +- source preprocessing +- 16 and 32 bit support +- LANGUAGE support (32 bit only) +- almost all resource types are supported +- enhanced expression capabilities and resource naming +- indirect loadable resources +- NE/PE resource directory generation +- binary .res file generation/reading + +Wrc generates an assembly file that can be assembled with GNU's gas, or +passed to gcc. The assembly became necessary for two reasons. First, C does +not ensure relative position of declared data. Secondly, C complaints about +complex initialization schemes that became necessary with the NE/PE +directory generation. + + +Wrc command-line +---------------- +You can get this message by typing 'wrc -?': + +Usage: wrc [options...] [infile[.rc|.res]] + -a n Alignment of resource (win16 only, default is 4) + -b Create a C array from a binary .res file + -c Add 'const' prefix to C constants + -C cp Set the resource's codepage to cp (default is 0) + -d n Set debug level to 'n' + -D id[=val] Define preprocessor identifier id=val + -e Disable recognition of win32 keywords in 16bit compile + -g Add symbols to the global c namespace + -h Also generate a .h file + -H file Same as -h but written to file + -I path Set include search dir to path (multiple -I allowed) + -l lan Set default language to lan (default is neutral {0}) + -n Do not generate .s file + -o file Output to file (default is infile.[res|s|h] + -p prefix Give a prefix for the generated names + -r Create binary .res file (compile only) + -s Add structure with win32/16 (PE/NE) resource directory + -t Generate indirect loadable resource tables + -T Generate only indirect loadable resources tables + -V Print version end exit + -w 16|32 Select win16 or win32 output (default is win32) + -W Enable pedantic warnings + +Input is taken from stdin if no sourcefile specified. + +Debug level 'n' is a bitmask with following meaning: + * 0x01 Tell which resource is parsed (verbose mode) + * 0x02 Dump internal structures + * 0x04 Create a parser trace (yydebug=1) + +The -o option only applies to the final destination file, which is +in case of normal compile a .s file. You must use the '-H header.h' +option to override the header-filename. +If no input filename is given and the output name is not overridden +with -o and/or -H, then the output is written to "wrc.tab.[sh]" + + +Preprocessing +------------- +The build-in preprocessor is not a full implementation of the C counterpart. +Wrc does not understand function-type macros. These are discarded as they +are scanned. This will be a future project. Wrc does understand these: +#define +#if +#ifdef +#ifndef +#elif +#else +#endif +#error + +Also 'defined' is supported as operator (both with and without parenthesis). +'#if' expressions can be anything valid that evaluates to an integer +expression (where 0 is false and anything else is true). Others (#pragma, +#line) are ignored. A special case '#' generates an error. This is due to +the implementation to enable generation of errors on preprocessing and will +be improved in the future. + + +16 and 32 bit support +--------------------- +All of wrc is layed out in such a way that it enables compilation of both 16 +and 32 bit resources. They mainly differ in code-generation and extra +keywords. Win32 keywords are recognized by default in 16 bit compile. You +can disable recognition of win32 reserved keywords by using the '-e' option, +if you encounter .rc-files that use win32 reserved keywords (I strongly +recommend that you just rename things in the source). + + +Language support +---------------- +Wrc also understands the LANGUAGE keyword (win32 only) for both global and +local definitions of language. There are differences with respect to MS' and +Borland's implementation. Wrc uses 0,0 as the default language if non is +specified. Both MS and Borland use the language of the system that the +compiler runs on. + +Not all resource-types can have local language keywords attached yet +(notably: BITMAP, CURSOR, ICON and usertype). This is due to implementation +of filename-scanning and the complexity that it poses. This will be changed +in the next release. + + +Resource types supported +------------------------ +All types are supported except for: +- FONT +- MESSAGETABLE +- extensions like TOOLBAR and the like + +These types will be implemented as soon as I get a proper specification of +the layout. + +Note: Usertype resources with character strings as types have a different +layout and do not accept expressions when a numerical type is specified. The +must be enclosed in double quotes. These are examples of valid usertype +resources: + +MyName "MyType" mydata.bin +MyName 12345 mydata.bin +MyName "MyType" "mydata.bin" +MyName 12345 "mydata.bin" + +MyName "MyType" +{ + ..., data, ... +} + +or + +MyName 12345 +{ + ..., data, ... +} + + +Expression capabilities and resource names +------------------------------------------ +You can use an expression in most places where the resource definition +expects a number (except usertype type). Operators supported: +() parenthesis +* multiply +/ divide ++ add +- minus/substract +| binary or +& binary and +~ binary not (unary operator though) +NOT ... (sigh) + +Minus (-) can both be unary and binary. The NOT operator is (primarily) +used to disable window styles but I strongly suggest to refrain from using +this operator. +There is a shift/reduce conflict on the unary minus, but this is not +problematic. I was too lazy to make a new expression parser (next version or +so). Unary plus (+) would cause another confilct, so I let it out for now. + +Resource names can be both numerical (expressions) and character typed. Wrc +does supports this insane (deep sigh) construct: + +MENU MENU +{ + ... +} + +It is _ONLY_ supported for backwards compatibility so that old sources can +be compiled with winelib. DO NOT USE IT IN NEW RESOURCES, PLEASE! + + +Indirect loadable resources +--------------------------- + +Wrc can generate tables for indirect resource loading like winerc did. There +are two new structures defined in 'wine-base-dir/include/wrc_rsc.h': + +typedef struct wrc_resource16 +{ + INT32 resid; /* The resource id if resname == NULL */ + LPSTR resname; + INT32 restype; /* The resource type-id if typename == NULL */ + LPSTR typename; + LPBYTE data; /* Actual resource data */ + UINT32 datasize; /* The size of the resource */ +} wrc_resource16_t; + +typedef struct wrc_resource32 +{ + INT32 resid; /* The resource id if resname == NULL */ + LPWSTR resname; + INT32 restype; /* The resource type-id if typename == NULL */ + LPWSTR typename; + LPBYTE data; /* Actual resource data */ + UINT32 datasize; /* The size of the resource */ +} wrc_resource32_t; + +The extension to winerc lies in the addition of the 'typename' field to +support usertype resoursec with names for types. + +Note that _ALL_ names generated by wrc and to be used in interfacing with +wine are PASCAL-style strings, unlike winerc. The first element contains the +length and the strings are _not_ '\0'-terminated! + +You can also generate header files with wrc when specifying the '-h' or +'-H<filename>' option. + + +NE/PE resource directory generation +----------------------------------- +A windows executable has a table/directory of resources avalable in that +module. Wrc will generate this directory with the '-s' option and place it +in the assembly output (and header-file). This will enable the separation +of different modules (dlls) in wine, which is the next project after wrc. + +The layout of the PE directory should be exactly like the executable file. +The NE-directory layout _DIFFERS_ from the real NE-executable in such way +that all offsets to actual resource-data is relative to the NE-directory and +_NOT_ the beginning of the file. + + +Binary .res file generation/reading +----------------------------------- +Wrc can both generate (32 and 16 bit) and read (32 bit only) .res-files. +These can be used as intermediate files or binary files can be imported from +other sources. The reading of 16 bit .res-files is on the list for the next +release. + +You cannot convert 32 bit .res-files into 16 bit output or vice versa. I +might implement 16 bit res into 32 bit output in the future, but I stronly +oppose to the other way arround. + + +Bugs +---- +Inherent to programs you have bugs. These I know are there, plus a few +things that I noted in the above text (more lack of implementation than bug +though): +- No codepage translation +- UNICODE translations are not/not correct implemented +- No documentation ('wrc -?' gives command-line options though) +- grep for FIXME in the source + + +Reporting bugs and patches +-------------------------- +Send problems to the wine newsgroup or, preferrably, directly to me at: + +bertho@akhphd.au.dk + +Please send the problematic rc-source with the bug so I can reproduce it. +Patches should _not_ be send to Alexandre but to me. I will then review the +change and send a full patch to be included into the new wine release (I +prefer 'diff -u' format). You can always upload suggestions to wine +headquarters, but be sure to send me a copy. +
diff --git a/tools/wrc/dumpres.c b/tools/wrc/dumpres.c new file mode 100644 index 0000000..33c09f4 --- /dev/null +++ b/tools/wrc/dumpres.c
@@ -0,0 +1,907 @@ +/* + * Copyrignt 1998 Bertho A. Stultiens (BS) + * + * 16-Apr-1998 BS - Yeah, dump it to stdout. + * + */ + +#include <stdio.h> +#include <ctype.h> + +#include <config.h> +#include "wrc.h" +#include "dumpres.h" + +/* + ***************************************************************************** + * Function : get_typename + * Syntax : char *get_typename(resource_t* r) + * Input : + * r - Resource description + * Output : A pointer to a string representing the resource type + * Description : + * Remarks : + ***************************************************************************** +*/ +char *get_typename(resource_t* r) +{ + switch(r->type){ + case res_acc: return "ACCELERATOR"; + case res_bmp: return "BITMAP"; + case res_cur: return "CURSOR"; + case res_curg: return "GROUP_CURSOR"; + case res_dlg: return "DIALOG"; + case res_dlgex: return "DIALOGEX"; + case res_fnt: return "FONT"; + case res_ico: return "ICON"; + case res_icog: return "GROUP_ICON"; + case res_men: return "MENU"; + case res_menex: return "MENUEX"; + case res_rdt: return "RCDATA"; + case res_stt: return "STRINGTABLE"; + case res_usr: return "UserResource"; + case res_msg: return "MESSAGETABLE"; + case res_ver: return "VERSIONINFO"; + default: return "Unknown"; + } +} + +/* + ***************************************************************************** + * Function : strncpyWtoA + * Syntax : char *strncpyWtoA(char *cs, short *ws, int maxlen) + * Input : + * cs - Pointer to buffer to receive result + * ws - Source wide-string + * maxlen - Max chars to copy + * Output : 'cs' + * Description : Copy a unicode string to ascii. Copying stops after the + * first occuring '\0' or when maxlen-1 chars are copied. The + * String is always nul terminated. + * Remarks : No codepage translation is done. + ***************************************************************************** +*/ +char *strncpyWtoA(char *cs, short *ws, int maxlen) +{ + char *cptr = cs; + while(*ws && maxlen-1) + { + if(*ws < -128 || *ws > 127) + printf("***Warning: Unicode string contains non-printable chars***"); + *cptr++ = (char)*ws++; + maxlen--; + } + *cptr = '\0'; + return cs; +} + +/* + ***************************************************************************** + * Function : print_string + * Syntax : void print_string(string_t *str) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void print_string(string_t *str) +{ + char buffer[512]; + if(!str) + printf("<none>"); + else if(str->type == str_char) + printf("\"%s\"", str->str.cstr); + else + { + strncpyWtoA(buffer, str->str.wstr, sizeof(buffer)); + printf("L\"%s\"", buffer); + } +} + +/* + ***************************************************************************** + * Function : get_nameid_str + * Syntax : char *get_nameid_str(name_id_t *n) + * Input : + * n - nameid to convert to text + * Output : A pointer to the name. + * Description : + * Remarks : Not reentrant because of static buffer + ***************************************************************************** +*/ +char *get_nameid_str(name_id_t *n) +{ + static char buffer[256]; + + if(!n) + return "<none>"; + + if(n->type == name_ord) + { + sprintf(buffer, "%d", n->name.i_name); + return buffer; + } + else if(n->type == name_str) + { + if(n->name.s_name->type == str_char) + return n->name.s_name->str.cstr; + else + { + strncpyWtoA(buffer, n->name.s_name->str.wstr, sizeof(buffer)); + return buffer; + } + } + else + return "Hoooo, report this: wrong type in nameid"; +} + +/* + ***************************************************************************** + * Function : dump_memopt + * Syntax : void dump_memopt(DWORD memopt) + * Input : + * memopt - flag bits of the options set + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_memopt(DWORD memopt) +{ + printf("Memory/load options: "); + if(memopt & 0x0040) + printf("PRELOAD "); + else + printf("LOADONCALL "); + if(memopt & 0x0010) + printf("MOVEABLE "); + else + printf("FIXED "); + if(memopt & 0x0020) + printf("PURE "); + else + printf("IMPURE "); + if(memopt & 0x1000) + printf("DISCARDABLE"); + printf("\n"); +} + +/* + ***************************************************************************** + * Function : dump_lvc + * Syntax : void dump_lvc(lvc_t *l) + * Input : + * l - pointer to lvc structure + * Output : + * Description : Dump language, version and characteristics + * Remarks : + ***************************************************************************** +*/ +void dump_lvc(lvc_t *l) +{ + if(l->language) + printf("LANGUAGE %04x, %04x\n", l->language->id, l->language->sub); + else + printf("LANGUAGE <not set>\n"); + + if(l->version) + printf("VERSION %08lx\n", *(l->version)); + else + printf("VERSION <not set>\n"); + + if(l->characts) + printf("CHARACTERISTICS %08lx\n", *(l->characts)); + else + printf("CHARACTERISTICS <not set>\n"); +} + +/* + ***************************************************************************** + * Function : dump_raw_data + * Syntax : void dump_raw_data(raw_data_t *d) + * Input : + * d - Raw data descriptor + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_raw_data(raw_data_t *d) +{ + int n; + int i; + int j; + + if(!d) + { + printf("<none>"); + return; + } + printf("Rawdata size: %d\n", d->size); + if(debuglevel < 2) + return; + + for(n = 0; n < d->size; n++) + { + if((n % 16) == 0) + if(n) + { + printf("- "); + for(i = 0; i < 16; i++) + printf("%c", isprint(d->data[n-16+i]) ? d->data[n-16+i] : '.'); + printf("\n%08x: ", n); + } + else + printf("%08x: ", n); + printf("%02x ", d->data[n] & 0xff); + } + printf("- "); + j = d->size % 16; + if(!j) + j = 16; + for(i = 0; i < j; i++) + printf("%c", isprint(d->data[n-j+i]) ? d->data[n-j+i] : '.'); + printf("\n"); +} + +/* + ***************************************************************************** + * Function : dump_accelerator + * Syntax : void dump_accelerator(resource_t *acc) + * Input : + * acc - Accelerator resource descriptor + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_accelerator(accelerator_t *acc) +{ + event_t *ev = acc->events; + + dump_memopt(acc->memopt); + dump_lvc(&(acc->lvc)); + + printf("Events: %s\n", ev ? "" : "<none>"); + while(ev) + { + printf("Key="); + if(isprint(ev->key)) + printf("\"%c\"", ev->key); + else if(iscntrl(ev->key)) + printf("\"^%c\"", ev->key +'@'); + else + printf("\\x%02x", ev->key & 0xff); + + printf(" Id=%d flags=%04x\n", ev->id, ev->flags); + ev = ev->next; + } +} + +/* + ***************************************************************************** + * Function : dump_cursor + * Syntax : void dump_cursor(cursor_t *cur) + * Input : + * cur - Cursor resource descriptor + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_cursor(cursor_t *cur) +{ + printf("Id: %d\n", cur->id); + printf("Width: %d\n", cur->width); + printf("Height: %d\n", cur->height); + printf("X Hotspot: %d\n", cur->xhot); + printf("Y Hotspot: %d\n", cur->yhot); + dump_raw_data(cur->data); +} + +/* + ***************************************************************************** + * Function : dump_cursor_group + * Syntax : void dump_cursor_group(cursor_group_t *cur) + * Input : + * cur - Cursor group resource descriptor + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_cursor_group(cursor_group_t *curg) +{ + dump_memopt(curg->memopt); + printf("There are %d cursors in this group\n", curg->ncursor); +} + +/* + ***************************************************************************** + * Function : dump_icon + * Syntax : void dump_icon(icon_t *ico) + * Input : + * ico - Icon resource descriptor + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_icon(icon_t *ico) +{ + printf("Id: %d\n", ico->id); + printf("Width: %d\n", ico->width); + printf("Height: %d\n", ico->height); + printf("NColor: %d\n", ico->nclr); + printf("NPlanes: %d\n", ico->planes); + printf("NBits: %d\n", ico->bits); + dump_raw_data(ico->data); +} + +/* + ***************************************************************************** + * Function : dump_icon_group + * Syntax : void dump_icon_group(icon_group_t *ico) + * Input : + * ico - Icon group resource descriptor + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_icon_group(icon_group_t *icog) +{ + dump_memopt(icog->memopt); + printf("There are %d icons in this group\n", icog->nicon); +} + +/* + ***************************************************************************** + * Function : dump_font + * Syntax : void dump_font(font_t *fnt) + * Input : + * fnt - Font resource descriptor + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_font(font_t *fnt) +{ + dump_memopt(fnt->memopt); + dump_raw_data(fnt->data); +} + +/* + ***************************************************************************** + * Function : dump_bitmap + * Syntax : void dump_bitmap(bitmap_t *bmp) + * Input : + * bmp - Bitmap resource descriptor + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_bitmap(bitmap_t *bmp) +{ + dump_memopt(bmp->memopt); + dump_raw_data(bmp->data); +} + +/* + ***************************************************************************** + * Function : dump_rcdata + * Syntax : void dump_rcdata(rcdata_t *rdt) + * Input : + * rdt - RCData resource descriptor + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_rcdata(rcdata_t *rdt) +{ + dump_memopt(rdt->memopt); + dump_raw_data(rdt->data); +} + +/* + ***************************************************************************** + * Function : dump_user + * Syntax : void dump_user(user_t *usr) + * Input : + * usr - User resource descriptor + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_user(user_t *usr) +{ + dump_memopt(usr->memopt); + printf("Class %s\n", get_nameid_str(usr->type)); + dump_raw_data(usr->data); +} + +/* + ***************************************************************************** + * Function : dump_messagetable + * Syntax : void dump_messagetable(messagetable_t *msg) + * Input : + * msg - Messagetable resource descriptor + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_messagetable(messagetable_t *msg) +{ + dump_raw_data(msg->data); +} + +/* + ***************************************************************************** + * Function : dump_stringtable + * Syntax : void dump_stringtable(stringtable_t *stt) + * Input : + * stt - Stringtable resource descriptor + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_stringtable(stringtable_t *stt) +{ + int i; + for(; stt; stt = stt->next) + { + printf("{\n"); + dump_memopt(stt->memopt); + dump_lvc(&(stt->lvc)); + for(i = 0; i < stt->nentries; i++) + { + printf("Id=%-5d (%d) ", stt->idbase+i, stt->entries[i].id); + if(stt->entries[i].str) + print_string(stt->entries[i].str); + else + printf("<none>"); + printf("\n"); + } + printf("}\n"); + } +} + +/* + ***************************************************************************** + * Function : dump_control + * Syntax : void dump_control(control_t *ctrl) + * Input : + * ctrl - Control resource descriptor + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_control(control_t *ctrl) +{ + printf("Control {\n\tClass: %s\n", get_nameid_str(ctrl->ctlclass)); + printf("\tText: "); print_string(ctrl->title); printf("\n"); + printf("\tId: %d\n", ctrl->id); + printf("\tx, y, w, h: %d, %d, %d, %d\n", ctrl->x, ctrl->y, ctrl->width, ctrl->height); + if(ctrl->gotstyle) + printf("\tStyle: %08lx\n", ctrl->style); + if(ctrl->gotexstyle) + printf("\tExStyle: %08lx\n", ctrl->exstyle); + if(ctrl->gothelpid) + printf("\tHelpid: %ld\n", ctrl->helpid); + if(ctrl->extra) + { + printf("\t"); + dump_raw_data(ctrl->extra); + } + printf("}\n"); +} + +/* + ***************************************************************************** + * Function : dump_dialog + * Syntax : void dump_dialog(dialog_t *dlg) + * Input : + * dlg - Dialog resource descriptor + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_dialog(dialog_t *dlg) +{ + control_t *c = dlg->controls; + + dump_memopt(dlg->memopt); + dump_lvc(&(dlg->lvc)); + printf("x, y, w, h: %d, %d, %d, %d\n", dlg->x, dlg->y, dlg->width, dlg->height); + if(dlg->gotstyle) + printf("Style: %08lx\n", dlg->style); + if(dlg->gotexstyle) + printf("ExStyle: %08lx\n", dlg->exstyle); + printf("Menu: %s\n", get_nameid_str(dlg->menu)); + printf("Class: %s\n", get_nameid_str(dlg->dlgclass)); + printf("Title: "); print_string(dlg->title); printf("\n"); + printf("Font: "); + if(!dlg->font) + printf("<none>\n"); + else + { + printf("%d, ", dlg->font->size); + print_string(dlg->font->name); + printf("\n"); + } + while(c) + { + dump_control(c); + c = c->next; + } +} + +/* + ***************************************************************************** + * Function : dump_dialogex + * Syntax : void dump_dialogex(dialogex_t *dlgex) + * Input : + * dlgex - DialogEx resource descriptor + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_dialogex(dialogex_t *dlgex) +{ + control_t *c = dlgex->controls; + + dump_memopt(dlgex->memopt); + dump_lvc(&(dlgex->lvc)); + printf("x, y, w, h: %d, %d, %d, %d\n", dlgex->x, dlgex->y, dlgex->width, dlgex->height); + if(dlgex->gotstyle) + printf("Style: %08lx\n", dlgex->style); + if(dlgex->gotexstyle) + printf("ExStyle: %08lx\n", dlgex->exstyle); + if(dlgex->gothelpid) + printf("Helpid: %ld\n", dlgex->helpid); + printf("Menu: %s\n", get_nameid_str(dlgex->menu)); + printf("Class: %s\n", get_nameid_str(dlgex->dlgclass)); + printf("Title: "); print_string(dlgex->title); printf("\n"); + printf("Font: "); + if(!dlgex->font) + printf("<none>\n"); + else + { + printf("%d, ", dlgex->font->size); + print_string(dlgex->font->name); + printf(", %d, %d\n", dlgex->font->weight, dlgex->font->italic); + } + while(c) + { + dump_control(c); + c = c->next; + } +} + +/* + ***************************************************************************** + * Function : dump_menu_item + * Syntax : void dump_menu_item(menu_item_t *item) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_menu_item(menu_item_t *item) +{ + while(item) + { + if(item->popup) + { + printf("POPUP "); + print_string(item->name); + printf("\n"); + dump_menu_item(item->popup); + } + else + { + printf("MENUITEM "); + if(item->name) + { + print_string(item->name); + printf(", %d, %08lx", item->id, item->state); + } + else + printf("SEPARATOR"); + printf("\n"); + } + item = item->next; + } +} + +/* + ***************************************************************************** + * Function : dump_menu + * Syntax : void dump_menu(menu_t *men) + * Input : + * men - Menu resource descriptor + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_menu(menu_t *men) +{ + dump_memopt(men->memopt); + dump_lvc(&(men->lvc)); + dump_menu_item(men->items); +} + +/* + ***************************************************************************** + * Function : dump_menuex_item + * Syntax : void dump_menuex_item(menuex_item_t *item) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_menuex_item(menuex_item_t *item) +{ + while(item) + { + if(item->popup) + { + printf("POPUP "); + print_string(item->name); + if(item->gotid) + printf(", Id=%d", item->id); + if(item->gottype) + printf(", Type=%ld", item->type); + if(item->gotstate) + printf(", State=%08lx", item->state); + if(item->gothelpid) + printf(", HelpId=%d", item->helpid); + printf("\n"); + dump_menuex_item(item->popup); + } + else + { + printf("MENUITEM "); + if(item->name) + { + print_string(item->name); + if(item->gotid) + printf(", Id=%d", item->id); + if(item->gottype) + printf(", Type=%ld", item->type); + if(item->gotstate) + printf(", State=%08lx", item->state); + if(item->gothelpid) + printf(", HelpId=%d", item->helpid); + } + else + printf("SEPARATOR"); + printf("\n"); + } + item = item->next; + } +} + +/* + ***************************************************************************** + * Function : dump_menuex + * Syntax : void dump_menuex(dialogex_t *menex) + * Input : + * menex - MenuEx resource descriptor + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_menuex(menuex_t *menex) +{ + dump_memopt(menex->memopt); + dump_lvc(&(menex->lvc)); + dump_menuex_item(menex->items); +} + +/* + ***************************************************************************** + * Function : dump_ver_value + * Syntax : void dump_ver_value(ver_value_t *val) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_ver_value(ver_value_t *val) +{ + extern void dump_ver_block(ver_block_t *); + if(val->type == val_str) + { + printf("VALUE "); + print_string(val->key); + printf(" "); + print_string(val->value.str); + printf("\n"); + } + else if(val->type == val_words) + { + int i; + printf("VALUE"); + print_string(val->key); + for(i = 0; i < val->value.words->nwords; i++) + printf(" %04x", val->value.words->words[i]); + printf("\n"); + } + else if(val->type == val_block) + { + dump_ver_block(val->value.block); + } +} + +/* + ***************************************************************************** + * Function : dump_ver_block + * Syntax : void dump_ver_block(ver_block_t *blk) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_ver_block(ver_block_t *blk) +{ + ver_value_t *val = blk->values; + printf("BLOCK "); + print_string(blk->name); + printf("\n{\n"); + while(val) + { + dump_ver_value(val); + val = val->next; + } + printf("}\n"); +} + +/* + ***************************************************************************** + * Function : dump_versioninfo + * Syntax : void dump_versioninfo(versioninfo_t *ver) + * Input : + * ver - Versioninfo resource descriptor + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void dump_versioninfo(versioninfo_t *ver) +{ + ver_block_t *blk = ver->blocks; + + if(ver->gotit.fv) + printf("FILEVERSION %04x, %04x, %04x, %04x\n", + ver->filever_maj1, + ver->filever_maj2, + ver->filever_min1, + ver->filever_min2); + if(ver->gotit.pv) + printf("PRODUCTVERSION %04x, %04x, %04x, %04x\n", + ver->prodver_maj1, + ver->prodver_maj2, + ver->prodver_min1, + ver->prodver_min2); + if(ver->gotit.fo) + printf("FILEOS %08x\n", ver->fileos); + if(ver->gotit.ff) + printf("FILEFLAGS %08x\n", ver->fileflags); + if(ver->gotit.ffm) + printf("FILEFLAGSMASK %08x\n", ver->fileflagsmask); + if(ver->gotit.ft) + printf("FILETYPE %08x\n", ver->filetype); + if(ver->gotit.fst) + printf("FILESUBTYPE %08x\n", ver->filesubtype); + while(blk) + { + dump_ver_block(blk); + blk = blk->next; + } +} + +/* + ***************************************************************************** + * Function : + * Syntax : + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +/* + ***************************************************************************** + * Function : dump_resources + * Syntax : void dump_resources(resource_t *top) + * Input : + * top - Top of the resource tree + * Output : + * nop + * Description : Dump the parsed resource-tree to stdout + * Remarks : + ***************************************************************************** +*/ +void dump_resources(resource_t *top) +{ + printf("Internal resource-tree dump:\n"); + while(top) + { + printf("Resource: %s\nId: %s\n", + get_typename(top), + get_nameid_str(top->name)); + switch(top->type) + { + case res_acc: + dump_accelerator(top->res.acc); + break; + case res_bmp: + dump_bitmap(top->res.bmp); + break; + case res_cur: + dump_cursor(top->res.cur); + break; + case res_curg: + dump_cursor_group(top->res.curg); + break; + case res_dlg: + dump_dialog(top->res.dlg); + break; + case res_dlgex: + dump_dialogex(top->res.dlgex); + break; + case res_fnt: + dump_font(top->res.fnt); + break; + case res_icog: + dump_icon_group(top->res.icog); + break; + case res_ico: + dump_icon(top->res.ico); + break; + case res_men: + dump_menu(top->res.men); + break; + case res_menex: + dump_menuex(top->res.menex); + break; + case res_rdt: + dump_rcdata(top->res.rdt); + break; + case res_stt: + dump_stringtable(top->res.stt); + break; + case res_usr: + dump_user(top->res.usr); + break; + case res_msg: + dump_messagetable(top->res.msg); + break; + case res_ver: + dump_versioninfo(top->res.ver); + break; + default: + printf("Report this: Unknown resource type parsed %08x\n", top->type); + } + printf("\n"); + top = top->next; + } +} +
diff --git a/tools/wrc/dumpres.h b/tools/wrc/dumpres.h new file mode 100644 index 0000000..75e9650 --- /dev/null +++ b/tools/wrc/dumpres.h
@@ -0,0 +1,18 @@ +/* + * Dump resource prototypes + * + * Copyright 1998 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WRC_DUMPRES_H +#define __WRC_DUMPRES_H + +#ifndef __WRC_WRCTYPES_H +#include "wrctypes.h" +#endif + +char *get_typename(resource_t* r); +void dump_resources(resource_t *top); + +#endif
diff --git a/tools/wrc/genres.c b/tools/wrc/genres.c new file mode 100644 index 0000000..b9e831c --- /dev/null +++ b/tools/wrc/genres.c
@@ -0,0 +1,1668 @@ +/* + * Generate .res format from a resource-tree + * + * Copyright 1998 Bertho A. Stultiens + * + * 25-May-1998 BS - Added simple unicode -> char conversion for resource + * names in .s and .h files. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> + +#include <config.h> +#include "wrc.h" +#include "genres.h" +#include "utils.h" + +#define SetResSize(res, tag) *(DWORD *)&((res)->data[(tag)]) = \ + (res)->size - *(DWORD *)&((res)->data[(tag)]) + +res_t *new_res(void) +{ + res_t *r; + r = (res_t *)xmalloc(sizeof(res_t)); + r->allocsize = RES_BLOCKSIZE; + r->size = 0; + r->data = (char *)xmalloc(RES_BLOCKSIZE); + return r; +} + +res_t *grow_res(res_t *r, int add) +{ + r->allocsize += add; + r->data = (char *)xrealloc(r->data, r->allocsize); + return r; +} + +/* + ***************************************************************************** + * Function : put_byte + * put_word + * put_dword + * Syntax : void put_byte(res_t *res, unsigned c) + * void put_word(res_t *res, unsigned w) + * void put_dword(res_t *res, unsigned d) + * Input : + * res - Binary resource to put the data in + * c, w, d - Data to put + * Output : nop + * Description : Put primitives that put an item in the binary resource. + * The data array grows automatically. + * Remarks : + ***************************************************************************** +*/ +void put_byte(res_t *res, unsigned c) +{ + if(res->allocsize - res->size < sizeof(char)) + grow_res(res, RES_BLOCKSIZE); + *(char *)&(res->data[res->size]) = (char)c; + res->size += sizeof(char); +} + +void put_word(res_t *res, unsigned w) +{ + if(res->allocsize - res->size < sizeof(WORD)) + grow_res(res, RES_BLOCKSIZE); + *(WORD *)&(res->data[res->size]) = (WORD)w; + res->size += sizeof(WORD); +} + +void put_dword(res_t *res, unsigned d) +{ + if(res->allocsize - res->size < sizeof(DWORD)) + grow_res(res, RES_BLOCKSIZE); + *(DWORD *)&(res->data[res->size]) = (DWORD)d; + res->size += sizeof(DWORD); +} + +void put_pad(res_t *res) +{ + while(res->size & 0x3) + put_byte(res, 0); +} + +/* + ***************************************************************************** + * Function : string_to_upper + * Syntax : void string_to_upper(string_t *str) + * Input : + * Output : + * Description : + * Remarks : FIXME: codepages... + ***************************************************************************** +*/ +void string_to_upper(string_t *str) +{ + if(str->type == str_char) + { + char *cptr = str->str.cstr; + for(; *cptr; cptr++) + *cptr = (char)toupper(*cptr); + } + else if(str->type == str_unicode) + { + short *sptr = str->str.wstr; + for(; *sptr; sptr++) + if(isalpha(*sptr)) + *sptr = (short)toupper(*sptr); + } + else + { + internal_error(__FILE__, __LINE__, "Invalid string type %d", str->type); + } +} + +/* + ***************************************************************************** + * Function : put_string + * Syntax : void put_string(res_t *res, string_t *str, enum str_e type, + * int isterm) + * Input : + * res - Binary resource to put the data in + * str - String to put + * type - Data has to be written in either str_char or str_unicode + * isterm - The string is '\0' terminated (disregard the string's + * size member) + * Output : nop + * Description : + * Remarks : + ***************************************************************************** +*/ +void put_string(res_t *res, string_t *str, enum str_e type, int isterm) +{ + int cnt; + int c = !0; + assert(res != NULL); + assert(str != NULL); + if(!isterm && str->size == 0) + { + warning("String length is zero, not written"); + return; + } + + if(str->type == str_unicode && type == str_unicode) + { + for(cnt = 0; cnt < str->size; cnt++) + { + c = str->str.wstr[cnt]; + put_word(res, c); + if(isterm && !c) + break; + } + if(isterm && (str->size == 0 || (cnt == str->size && c))) + put_word(res, 0); + } + else if(str->type == str_char && type == str_char) + { + for(cnt = 0; cnt < str->size; cnt++) + { + c = str->str.cstr[cnt]; + put_byte(res, c); + if(isterm && !c) + break; + } + if(isterm && (str->size == 0 || (cnt == str->size && c))) + put_byte(res, 0); + } + else if(str->type == str_unicode && type == str_char) + { + for(cnt = 0; cnt < str->size; cnt++) + { + c = str->str.wstr[cnt]; + put_byte(res, c); + if(isterm && !c) + break; + } + if(isterm && (str->size == 0 || (cnt == str->size && c))) + put_byte(res, 0); + } + else /* str->type == str_char && type == str_unicode */ + { + for(cnt = 0; cnt < str->size; cnt++) + { + c = str->str.cstr[cnt]; + put_word(res, c & 0xff); + if(isterm && !c) + break; + } + if(isterm && (str->size == 0 || (cnt == str->size && c))) + put_word(res, 0); + } +} + +/* + ***************************************************************************** + * Function : put_name_id + * Syntax : void put_name_id(res_t *res, name_id_t *nid, int upcase) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void put_name_id(res_t *res, name_id_t *nid, int upcase) +{ + if(nid->type == name_ord) + { + if(win32) + put_word(res, 0xffff); + else + put_byte(res, 0xff); + put_word(res, (WORD)nid->name.i_name); + } + else if(nid->type == name_str) + { + if(upcase) + string_to_upper(nid->name.s_name); + put_string(res, nid->name.s_name, win32 ? str_unicode : str_char, TRUE); + } + else + { + internal_error(__FILE__, __LINE__, "Invalid name_id type %d", nid->type); + } +} + +/* + ***************************************************************************** + * Function : put_lvc + * Syntax : void put_lvc(res_t *res, lvc_t *lvc) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void put_lvc(res_t *res, lvc_t *lvc) +{ + if(lvc && lvc->language) + put_word(res, MAKELANGID(lvc->language->id, lvc->language->sub)); + else + put_word(res, 0); /* Neutral */ + if(lvc && lvc->version) + put_dword(res, *(lvc->version)); + else + put_dword(res, 0); + if(lvc && lvc->characts) + put_dword(res, *(lvc->characts)); + else + put_dword(res, 0); +} + +/* + ***************************************************************************** + * Function : put_raw_data + * Syntax : void put_raw_data(res_t *res, raw_data_t *raw, int offset) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void put_raw_data(res_t *res, raw_data_t *raw, int offset) +{ + int wsize = raw->size - offset; + if(res->allocsize - res->size < wsize) + grow_res(res, wsize); + memcpy(&(res->data[res->size]), raw->data + offset, wsize); + res->size += wsize; +} + +/* + ***************************************************************************** + * Function : put_res_header + * Syntax : intput_res_header(res_t *res, int type, name_id_t *ntype, + * name_id_t *name, DWORD memopt, lvc_t *lvc) + * + * Input : + * res - Binary resource descriptor to write to + * type - Resource identifier (if ntype == NULL) + * ntype - Name id of type + * name - Resource's name + * memopt - Resource's memory options to write + * lvc - Language, version and characteristics (win32 only) + * Output : An index to the resource size field. The resource size field + * contains the header size upon exit. + * Description : + * Remarks : + ***************************************************************************** +*/ +int put_res_header(res_t *res, int type, name_id_t *ntype, name_id_t *name, + DWORD memopt, lvc_t *lvc) +{ + if(win32) + { + put_dword(res, 0); /* We will overwrite these later */ + put_dword(res, 0); + if(!ntype) + { + put_word(res, 0xffff); /* ResType */ + put_word(res, type); + } + else + put_name_id(res, ntype, TRUE); + put_name_id(res, name, TRUE); /* ResName */ + put_pad(res); + put_dword(res, 0); /* DataVersion */ + put_word(res, memopt); /* Memory options */ + put_lvc(res, lvc); /* Language, version and characts */ + ((DWORD *)res->data)[0] = res->size; /* Set preliminary resource */ + ((DWORD *)res->data)[1] = res->size; /* Set HeaderSize */ + res->dataidx = res->size; + return 0; + } + else /* win16 */ + { + int tag; + if(!ntype) + { + put_byte(res, 0xff); /* ResType */ + put_word(res, type); + } + else + put_name_id(res, ntype, TRUE); + put_name_id(res, name, TRUE); /* ResName */ + put_word(res, memopt); /* Memory options */ + tag = res->size; + put_dword(res, 0); /* ResSize overwritten later*/ + *(DWORD *)&(res->data[tag]) = res->size; + res->dataidx = res->size; + return tag; + } +} + +/* + ***************************************************************************** + * Function : accelerator2res + * Syntax : res_t *accelerator2res(name_id_t *name, accelerator_t *acc) + * Input : + * name - Name/ordinal of the resource + * acc - The accelerator descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *accelerator2res(name_id_t *name, accelerator_t *acc) +{ + int restag; + res_t *res; + event_t *ev; + assert(name != NULL); + assert(acc != NULL); + + ev = acc->events; + res = new_res(); + if(win32) + { + restag = put_res_header(res, WRC_RT_ACCELERATOR, NULL, name, acc->memopt, &(acc->lvc)); + while(ev) + { + put_word(res, ev->flags | (ev->next ? 0 : 0x80)); + put_word(res, ev->key); + put_word(res, ev->id); + put_word(res, 0); /* Padding */ + ev = ev->next; + } + put_pad(res); + } + else /* win16 */ + { + restag = put_res_header(res, WRC_RT_ACCELERATOR, NULL, name, acc->memopt, NULL); + while(ev) + { + put_byte(res, ev->flags | (ev->next ? 0 : 0x80)); + put_word(res, ev->key); + put_word(res, ev->id); + ev = ev->next; + } + } + /* Set ResourceSize */ + SetResSize(res, restag); + return res; +} + +/* + ***************************************************************************** + * Function : dialog2res + * Syntax : res_t *dialog2res(name_id_t *name, dialog_t *dlg) + * Input : + * name - Name/ordinal of the resource + * dlg - The dialog descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *dialog2res(name_id_t *name, dialog_t *dlg) +{ + int restag; + res_t *res; + control_t *ctrl; + int tag_nctrl; + int nctrl = 0; + assert(name != NULL); + assert(dlg != NULL); + + ctrl = dlg->controls; + res = new_res(); + if(win32) + { + restag = put_res_header(res, WRC_RT_DIALOG, NULL, name, dlg->memopt, &(dlg->lvc)); + + put_dword(res, dlg->style); + put_dword(res, dlg->gotexstyle ? dlg->exstyle : 0); + tag_nctrl = res->size; + put_word(res, 0); /* Number of controls */ + put_word(res, dlg->x); + put_word(res, dlg->y); + put_word(res, dlg->width); + put_word(res, dlg->height); + if(dlg->menu) + put_name_id(res, dlg->menu, TRUE); + else + put_word(res, 0); + if(dlg->dlgclass) + put_name_id(res, dlg->dlgclass, TRUE); + else + put_word(res, 0); + if(dlg->title) + put_string(res, dlg->title, str_unicode, TRUE); + else + put_word(res, 0); + if(dlg->font) + { + put_word(res, dlg->font->size); + put_string(res, dlg->font->name, str_unicode, TRUE); + } + + put_pad(res); + while(ctrl) + { + /* FIXME: what is default control style? */ + put_dword(res, ctrl->gotstyle ? ctrl->style : WS_CHILD); + put_dword(res, ctrl->gotexstyle ? ctrl->exstyle : 0); + put_word(res, ctrl->x); + put_word(res, ctrl->y); + put_word(res, ctrl->width); + put_word(res, ctrl->height); + put_word(res, ctrl->id); + if(ctrl->ctlclass) + put_name_id(res, ctrl->ctlclass, TRUE); + else + internal_error(__FILE__, __LINE__, "Control has no control-class"); + if(ctrl->title) + put_string(res, ctrl->title, str_unicode, TRUE); + else + put_word(res, 0); + if(ctrl->extra) + { + put_word(res, ctrl->extra->size+2); + put_pad(res); + put_raw_data(res, ctrl->extra, 0); + } + else + put_word(res, 0); + + if(ctrl->next) + put_pad(res); + nctrl++; + ctrl = ctrl->next; + } + /* Set number of controls */ + *(WORD *)&((char *)res->data)[tag_nctrl] = (WORD)nctrl; + } + else /* win16 */ + { + restag = put_res_header(res, WRC_RT_DIALOG, NULL, name, dlg->memopt, NULL); + + put_dword(res, dlg->gotstyle ? dlg->style : WS_POPUPWINDOW); + tag_nctrl = res->size; + put_byte(res, 0); /* Number of controls */ + put_word(res, dlg->x); + put_word(res, dlg->y); + put_word(res, dlg->width); + put_word(res, dlg->height); + if(dlg->menu) + put_name_id(res, dlg->menu, TRUE); + else + put_byte(res, 0); + if(dlg->dlgclass) + put_name_id(res, dlg->dlgclass, TRUE); + else + put_byte(res, 0); + if(dlg->title) + put_string(res, dlg->title, str_char, TRUE); + else + put_byte(res, 0); + if(dlg->font) + { + put_word(res, dlg->font->size); + put_string(res, dlg->font->name, str_char, TRUE); + } + + while(ctrl) + { + put_word(res, ctrl->x); + put_word(res, ctrl->y); + put_word(res, ctrl->width); + put_word(res, ctrl->height); + put_word(res, ctrl->id); + put_dword(res, ctrl->gotstyle ? ctrl->style : WS_CHILD); + if(ctrl->ctlclass) + { + if(ctrl->ctlclass->type == name_ord + && ctrl->ctlclass->name.i_name >= 0x80 + && ctrl->ctlclass->name.i_name <= 0x85) + put_byte(res, ctrl->ctlclass->name.i_name); + else if(ctrl->ctlclass->type == name_str) + put_name_id(res, ctrl->ctlclass, FALSE); + else + error("Unknown control-class %04x", ctrl->ctlclass->name.i_name); + } + else + internal_error(__FILE__, __LINE__, "Control has no control-class"); + if(ctrl->title) + put_string(res, ctrl->title, str_char, TRUE); + else + put_byte(res, 0); + + /* FIXME: What is this extra byte doing here? */ + put_byte(res, 0); + + nctrl++; + ctrl = ctrl->next; + } + /* Set number of controls */ + ((char *)res->data)[tag_nctrl] = (char)nctrl; + } + /* Set ResourceSize */ + SetResSize(res, restag); + return res; +} + +/* + ***************************************************************************** + * Function : dialogex2res + * Syntax : res_t *dialogex2res(name_id_t *name, dialogex_t *dlgex) + * Input : + * name - Name/ordinal of the resource + * dlgex - The dialogex descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *dialogex2res(name_id_t *name, dialogex_t *dlgex) +{ + int restag; + res_t *res; + control_t *ctrl; + int tag_nctrl; + int nctrl = 0; + assert(name != NULL); + assert(dlgex != NULL); + + ctrl = dlgex->controls; + res = new_res(); + if(win32) + { + restag = put_res_header(res, WRC_RT_DIALOG, NULL, name, dlgex->memopt, &(dlgex->lvc)); + + /* FIXME: MS doc says thet the first word must contain 0xffff + * and the second 0x0001 to signal a DLGTEMPLATEEX. Borland's + * compiler reverses the two words. + * I don't know which one to choose, but I write it as Mr. B + * writes it. + */ + put_word(res, 1); /* Signature */ + put_word(res, 0xffff); /* DlgVer */ + put_dword(res, dlgex->gothelpid ? dlgex->helpid : 0); + put_dword(res, dlgex->gotexstyle ? dlgex->exstyle : 0); + put_dword(res, dlgex->gotstyle ? dlgex->style : WS_POPUPWINDOW); + tag_nctrl = res->size; + put_word(res, 0); /* Number of controls */ + put_word(res, dlgex->x); + put_word(res, dlgex->y); + put_word(res, dlgex->width); + put_word(res, dlgex->height); + if(dlgex->menu) + put_name_id(res, dlgex->menu, TRUE); + else + put_word(res, 0); + if(dlgex->dlgclass) + put_name_id(res, dlgex->dlgclass, TRUE); + else + put_word(res, 0); + if(dlgex->title) + put_string(res, dlgex->title, str_unicode, TRUE); + else + put_word(res, 0); + if(dlgex->font) + { + put_word(res, dlgex->font->size); + put_word(res, dlgex->font->weight); + /* FIXME: ? TRUE should be sufficient to say that its + * italic, but Borland's compiler says its 0x0101. + * I just copy it here, and hope for the best. + */ + put_word(res, dlgex->font->italic ? 0x0101 : 0); + put_string(res, dlgex->font->name, str_unicode, TRUE); + } + + put_pad(res); + while(ctrl) + { + put_dword(res, ctrl->gothelpid ? ctrl->helpid : 0); + put_dword(res, ctrl->gotexstyle ? ctrl->exstyle : 0); + /* FIXME: what is default control style? */ + put_dword(res, ctrl->gotstyle ? ctrl->style : WS_CHILD | WS_VISIBLE); + put_word(res, ctrl->x); + put_word(res, ctrl->y); + put_word(res, ctrl->width); + put_word(res, ctrl->height); + put_word(res, ctrl->id); + /* FIXME: Pad is _NOT_ documented!?! */ + put_pad(res); + if(ctrl->ctlclass) + put_name_id(res, ctrl->ctlclass, TRUE); + else + internal_error(__FILE__, __LINE__, "Control has no control-class"); + if(ctrl->title) + put_string(res, ctrl->title, str_unicode, TRUE); + else + put_word(res, 0); + if(ctrl->extra) + { + put_pad(res); + put_word(res, ctrl->extra->size); + put_raw_data(res, ctrl->extra, 0); + } + else + put_word(res, 0); + + put_pad(res); + nctrl++; + ctrl = ctrl->next; + } + /* Set number of controls */ + *(WORD *)&((char *)res->data)[tag_nctrl] = (WORD)nctrl; + /* Set ResourceSize */ + SetResSize(res, restag); + put_pad(res); + } + else /* win16 */ + { + /* Do not generate anything in 16-bit mode */ + free(res->data); + free(res); + return NULL; + } + return res; +} + +/* + ***************************************************************************** + * Function : menuitem2res + * Syntax : void menuitem2res(res_t *res, menu_item_t *item) + * Input : + * Output : + * Description : + * Remarks : Self recursive + ***************************************************************************** +*/ +void menuitem2res(res_t *res, menu_item_t *menitem) +{ + menu_item_t *itm = menitem; + if(win32) + { + while(itm) + { + put_word(res, itm->state | (itm->popup ? MF_POPUP : 0) | (!itm->next ? MF_END : 0)); + if(!itm->popup) + put_word(res, itm->id); + if(itm->name) + put_string(res, itm->name, str_unicode, TRUE); + else + put_word(res, 0); + if(itm->popup) + menuitem2res(res, itm->popup); + itm = itm->next; + } + } + else /* win16 */ + { + while(itm) + { + put_word(res, itm->state | (itm->popup ? MF_POPUP : 0) | (!itm->next ? MF_END : 0)); + if(!itm->popup) + put_word(res, itm->id); + if(itm->name) + put_string(res, itm->name, str_char, TRUE); + else + put_byte(res, 0); + if(itm->popup) + menuitem2res(res, itm->popup); + itm = itm->next; + } + } + +} + +/* + ***************************************************************************** + * Function : menu2res + * Syntax : res_t *menu2res(name_id_t *name, menu_t *men) + * Input : + * name - Name/ordinal of the resource + * men - The menu descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *menu2res(name_id_t *name, menu_t *men) +{ + int restag; + res_t *res; + assert(name != NULL); + assert(men != NULL); + + res = new_res(); + restag = put_res_header(res, WRC_RT_MENU, NULL, name, men->memopt, win32 ? &(men->lvc) : NULL); + + put_dword(res, 0); /* Menuheader: Version and HeaderSize */ + menuitem2res(res, men->items); + /* Set ResourceSize */ + SetResSize(res, restag); + if(win32) + put_pad(res); + return res; +} + +/* + ***************************************************************************** + * Function : menuexitem2res + * Syntax : void menuexitem2res(res_t *res, menuex_item_t *item) + * Input : + * Output : nop + * Description : + * Remarks : Self recursive + ***************************************************************************** +*/ +void menuexitem2res(res_t *res, menuex_item_t *menitem) +{ + menuex_item_t *itm = menitem; + assert(win32 != 0); + while(itm) + { + put_dword(res, itm->gottype ? itm->type : 0); + put_dword(res, itm->gotstate ? itm->state : 0); + put_dword(res, itm->gotid ? itm->id : 0); /* FIXME: Docu. says word */ + put_word(res, (itm->popup ? 0x01 : 0) | (!itm->next ? MF_END : 0)); + if(itm->name) + put_string(res, itm->name, str_unicode, TRUE); + else + put_word(res, 0); + put_pad(res); + if(itm->popup) + { + put_dword(res, itm->gothelpid ? itm->helpid : 0); + menuexitem2res(res, itm->popup); + } + itm = itm->next; + } + +} + +/* + ***************************************************************************** + * Function : menuex2res + * Syntax : res_t *menuex2res(name_id_t *name, menuex_t *menex) + * Input : + * name - Name/ordinal of the resource + * menex - The menuex descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *menuex2res(name_id_t *name, menuex_t *menex) +{ + int restag; + res_t *res; + assert(name != NULL); + assert(menex != NULL); + + res = new_res(); + if(win32) + { + /* FIXME: Borland's rc compiler writes the default system's + * language if none specified (0x406 for me that is). I believe + * that this is a Borland bug (brc32 ver. 5.01), as the default + * should be 0 (system default). + */ + restag = put_res_header(res, WRC_RT_MENU, NULL, name, menex->memopt, &(menex->lvc)); + + put_word(res, 1); /* Menuheader: Version */ + put_word(res, 4); /* Offset */ + put_dword(res, 0); /* HelpId */ + put_pad(res); + menuexitem2res(res, menex->items); + /* Set ResourceSize */ + SetResSize(res, restag); + put_pad(res); + } + else /* win16 */ + { + /* Do not generate anything in 16-bit mode */ + free(res->data); + free(res); + return NULL; + } + return res; +} + +/* + ***************************************************************************** + * Function : cursorgroup2res + * Syntax : res_t *cursorgroup2res(name_id_t *name, cursor_group_t *curg) + * Input : + * name - Name/ordinal of the resource + * curg - The cursor descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *cursorgroup2res(name_id_t *name, cursor_group_t *curg) +{ + int restag; + res_t *res; + cursor_t *cur; + assert(name != NULL); + assert(curg != NULL); + + res = new_res(); + restag = put_res_header(res, WRC_RT_GROUP_CURSOR, NULL, name, curg->memopt, NULL); + if(win32) + { + /* FIXME: Borland's rc compiler writes the default system's + * language if none specified (0x406 for me that is). I believe + * that this is a Borland bug (brc32 ver. 5.01), as the default + * should be 0 (system default). + */ + + put_word(res, 0); /* Reserved */ + /* FIXME: The ResType in the NEWHEADER structure should + * contain 14 according to the MS win32 doc. This is + * not the case with the BRC compiler and I really doubt + * the latter. Putting one here is compliant to win16 spec, + * but who knows the true value? + */ + put_word(res, 2); /* ResType */ + put_word(res, curg->ncursor); +#if 0 + for(cur = curg->cursorlist; cur; cur = cur->next) +#else + cur = curg->cursorlist; + while(cur->next) + cur = cur->next; + for(; cur; cur = cur->prev) +#endif + { + put_word(res, cur->width); + /* FIXME: The height of a cursor is half the size of + * the bitmap's height. BRC puts the height from the + * BITMAPINFOHEADER here instead of the cursorfile's + * height. MS doesn't seem to care... + */ + put_word(res, cur->height); + /* FIXME: The next two are reversed in BRC and I don't + * know why. Probably a bug. But, we can safely ignore + * it because win16 does not support color cursors. + * A warning should have been generated by the parser. + */ + put_word(res, cur->planes); + put_word(res, cur->bits); + /* FIXME: The +4 is the hotspot in the cursor resource. + * However, I cound not find this in the documentation. + * The hotspot bytes must either be included or MS + * doesn't care. + */ + put_dword(res, cur->data->size +4); + put_word(res, cur->id); + } + } + else /* win16 */ + { + put_word(res, 0); /* Reserved */ + put_word(res, 2); /* ResType */ + put_word(res, curg->ncursor); +#if 0 + for(cur = curg->cursorlist; cur; cur = cur->next) +#else + cur = curg->cursorlist; + while(cur->next) + cur = cur->next; + for(; cur; cur = cur->prev) +#endif + { + put_word(res, cur->width); + /* FIXME: The height of a cursor is half the size of + * the bitmap's height. BRC puts the height from the + * BITMAPINFOHEADER here instead of the cursorfile's + * height. MS doesn't seem to care... + */ + put_word(res, cur->height); + /* FIXME: The next two are reversed in BRC and I don't + * know why. Probably a bug. But, we can safely ignore + * it because win16 does not support color cursors. + * A warning should have been generated by the parser. + */ + put_word(res, cur->planes); + put_word(res, cur->bits); + /* FIXME: The +4 is the hotspot in the cursor resource. + * However, I cound not find this in the documentation. + * The hotspot bytes must either be included or MS + * doesn't care. + */ + put_dword(res, cur->data->size +4); + put_word(res, cur->id); + } + } + SetResSize(res, restag); /* Set ResourceSize */ + if(win32) + put_pad(res); + + return res; +} + +/* + ***************************************************************************** + * Function : cursor2res + * Syntax : res_t *cursor2res(cursor_t *cur) + * Input : + * cur - The cursor descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *cursor2res(cursor_t *cur) +{ + int restag; + res_t *res; + name_id_t name; + + assert(cur != NULL); + + res = new_res(); + name.type = name_ord; + name.name.i_name = cur->id; + /* FIXME: Borland's rc compiler writes the default system's + * language if none specified (0x406 for me that is). I believe + * that this is a Borland bug (brc32 ver. 5.01), as the default + * should be 0 (system default). + */ + restag = put_res_header(res, WRC_RT_CURSOR, NULL, &name, WRC_MO_MOVEABLE | WRC_MO_DISCARDABLE, NULL); + put_word(res, cur->xhot); + put_word(res, cur->yhot); + put_raw_data(res, cur->data, 0); + + SetResSize(res, restag); /* Set ResourceSize */ + if(win32) + put_pad(res); + + return res; +} + +/* + ***************************************************************************** + * Function : icongroup2res + * Syntax : res_t *icongroup2res(name_id_t *name, icon_group_t *icog) + * Input : + * name - Name/ordinal of the resource + * icog - The icon group descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *icongroup2res(name_id_t *name, icon_group_t *icog) +{ + int restag; + res_t *res; + icon_t *ico; + assert(name != NULL); + assert(icog != NULL); + + res = new_res(); + restag = put_res_header(res, WRC_RT_GROUP_ICON, NULL, name, icog->memopt, NULL); + if(win32) + { + /* FIXME: Borland's rc compiler writes the default system's + * language if none specified (0x406 for me that is). I believe + * that this is a Borland bug (brc32 ver. 5.01), as the default + * should be 0 (system default). + */ + + put_word(res, 0); /* Reserved */ + /* FIXME: The ResType in the NEWHEADER structure should + * contain 14 according to the MS win32 doc. This is + * not the case with the BRC compiler and I really doubt + * the latter. Putting one here is compliant to win16 spec, + * but who knows the true value? + */ + put_word(res, 1); /* ResType */ + put_word(res, icog->nicon); + for(ico = icog->iconlist; ico; ico = ico->next) + { + put_byte(res, ico->width); + put_byte(res, ico->height); + put_byte(res, ico->nclr); + put_byte(res, 0); /* Reserved */ + put_word(res, ico->planes); + put_word(res, ico->bits); + put_dword(res, ico->data->size); + put_word(res, ico->id); + } + } + else /* win16 */ + { + put_word(res, 0); /* Reserved */ + put_word(res, 1); /* ResType */ + put_word(res, icog->nicon); + for(ico = icog->iconlist; ico; ico = ico->next) + { + put_byte(res, ico->width); + put_byte(res, ico->height); + put_byte(res, ico->nclr); + put_byte(res, 0); /* Reserved */ + put_word(res, ico->planes); + put_word(res, ico->bits); + put_dword(res, ico->data->size); + put_word(res, ico->id); + } + } + SetResSize(res, restag); /* Set ResourceSize */ + if(win32) + put_pad(res); + + return res; +} + +/* + ***************************************************************************** + * Function : icon2res + * Syntax : res_t *icon2res(icon_t *ico) + * Input : + * ico - The icon descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *icon2res(icon_t *ico) +{ + int restag; + res_t *res; + name_id_t name; + + assert(ico != NULL); + + res = new_res(); + name.type = name_ord; + name.name.i_name = ico->id; + /* FIXME: Borland's rc compiler writes the default system's + * language if none specified (0x406 for me that is). I believe + * that this is a Borland bug (brc32 ver. 5.01), as the default + * should be 0 (system default). + */ + restag = put_res_header(res, WRC_RT_ICON, NULL, &name, WRC_MO_MOVEABLE | WRC_MO_DISCARDABLE, NULL); + put_raw_data(res, ico->data, 0); + + SetResSize(res, restag); /* Set ResourceSize */ + if(win32) + put_pad(res); + + return res; +} + +/* + ***************************************************************************** + * Function : bitmap2res + * Syntax : res_t *bitmap2res(name_id_t *name, bitmap_t *bmp) + * Input : + * name - Name/ordinal of the resource + * bmp - The bitmap descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *bitmap2res(name_id_t *name, bitmap_t *bmp) +{ + int restag; + res_t *res; + assert(name != NULL); + assert(bmp != NULL); + + HEAPCHECK(); + res = new_res(); + HEAPCHECK(); + restag = put_res_header(res, WRC_RT_BITMAP, NULL, name, bmp->memopt, NULL); + HEAPCHECK(); + if(bmp->data->data[0] == 'B' + && bmp->data->data[1] == 'M' + && ((BITMAPFILEHEADER *)bmp->data->data)->bfSize == bmp->data->size + && bmp->data->size >= sizeof(BITMAPFILEHEADER)) + { + /* The File header is still attached, don't write it */ + put_raw_data(res, bmp->data, sizeof(BITMAPFILEHEADER)); + } + else + { + put_raw_data(res, bmp->data, 0); + } + HEAPCHECK(); + /* Set ResourceSize */ + SetResSize(res, restag); + HEAPCHECK(); + if(win32) + put_pad(res); + HEAPCHECK(); + return res; +} + +/* + ***************************************************************************** + * Function : font2res + * Syntax : res_t *font2res(name_id_t *name, font_t *fnt) + * Input : + * name - Name/ordinal of the resource + * fnt - The font descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *font2res(name_id_t *name, font_t *fnt) +{ + assert(name != NULL); + assert(fnt != NULL); + warning("Fonts not yet implemented"); + return NULL; +} + +/* + ***************************************************************************** + * Function : rcdata2res + * Syntax : res_t *rcdata2res(name_id_t *name, rcdata_t *rdt) + * Input : + * name - Name/ordinal of the resource + * rdt - The rcdata descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *rcdata2res(name_id_t *name, rcdata_t *rdt) +{ + int restag; + res_t *res; + assert(name != NULL); + assert(rdt != NULL); + + res = new_res(); + restag = put_res_header(res, WRC_RT_RCDATA, NULL, name, rdt->memopt, NULL); + put_raw_data(res, rdt->data, 0); + /* Set ResourceSize */ + SetResSize(res, restag); + if(win32) + put_pad(res); + return res; +} + +/* + ***************************************************************************** + * Function : messagetable2res + * Syntax : res_t *messagetable2res(name_id_t *name, messagetable_t *msg) + * Input : + * name - Name/ordinal of the resource + * msg - The messagetable descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *messagetable2res(name_id_t *name, messagetable_t *msg) +{ + assert(name != NULL); + assert(msg != NULL); + warning("Messagetable not yet implemented"); + return NULL; +} + +/* + ***************************************************************************** + * Function : stringtable2res + * Syntax : res_t *stringtable2res(stringtable_t *stt) + * Input : + * stt - The stringtable descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *stringtable2res(stringtable_t *stt) +{ + res_t *res; + name_id_t name; + int i; + int restag; + DWORD lastsize = 0; + + assert(stt != NULL); + res = new_res(); + + for(; stt; stt = stt->next) + { + if(!stt->nentries) + { + warning("Empty internal stringtable"); + continue; + } + name.type = name_ord; + name.name.i_name = (stt->idbase >> 4) + 1; + restag = put_res_header(res, WRC_RT_STRING, NULL, &name, stt->memopt, win32 ? &(stt->lvc) : NULL); + for(i = 0; i < stt->nentries; i++) + { + if(stt->entries[i].str) + { + if(win32) + put_word(res, stt->entries[i].str->size); + else + put_byte(res, stt->entries[i].str->size); + put_string(res, stt->entries[i].str, win32 ? str_unicode : str_char, FALSE); + } + else + { + if(win32) + put_word(res, 0); + else + put_byte(res, 0); + } + } + /* Set ResourceSize */ + SetResSize(res, restag - lastsize); + if(win32) + put_pad(res); + lastsize = res->size; + } + return res; +} + +/* + ***************************************************************************** + * Function : user2res + * Syntax : res_t *user2res(name_id_t *name, user_t *usr) + * Input : + * name - Name/ordinal of the resource + * usr - The userresource descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *user2res(name_id_t *name, user_t *usr) +{ + int restag; + res_t *res; + assert(name != NULL); + assert(usr != NULL); + + res = new_res(); + restag = put_res_header(res, 0, usr->type, name, usr->memopt, NULL); + put_raw_data(res, usr->data, 0); + /* Set ResourceSize */ + SetResSize(res, restag); + if(win32) + put_pad(res); + return res; +} + +/* + ***************************************************************************** + * Function : versionblock2res + * Syntax : void versionblock2res(res_t *res, ver_block_t *blk) + * Input : + * res - Binary resource to write to + * blk - The version block to be written + * Output : + * Description : + * Remarks : Self recursive + ***************************************************************************** +*/ +void versionblock2res(res_t *res, ver_block_t *blk, int level) +{ + ver_value_t *val; + int blksizetag; + int valblksizetag; + int valvalsizetag; + int tag; + int i; + + blksizetag = res->size; + put_word(res, 0); /* Will be overwritten later */ + put_word(res, 0); + if(win32) + put_word(res, 0); /* level ? */ + put_string(res, blk->name, win32 ? str_unicode : str_char, TRUE); + put_pad(res); + for(val = blk->values; val; val = val->next) + { + if(val->type == val_str) + { + valblksizetag = res->size; + put_word(res, 0); /* Will be overwritten later */ + valvalsizetag = res->size; + put_word(res, 0); /* Will be overwritten later */ + if(win32) + { + put_word(res, level); + } + put_string(res, val->key, win32 ? str_unicode : str_char, TRUE); + put_pad(res); + tag = res->size; + put_string(res, val->value.str, win32 ? str_unicode : str_char, TRUE); + if(win32) + *(WORD *)&(res->data[valvalsizetag]) = (WORD)((res->size - tag) >> 1); + else + *(WORD *)&(res->data[valvalsizetag]) = (WORD)(res->size - tag); + *(WORD *)&(res->data[valblksizetag]) = (WORD)(res->size - valblksizetag); + put_pad(res); + } + else if(val->type == val_words) + { + valblksizetag = res->size; + put_word(res, 0); /* Will be overwritten later */ + valvalsizetag = res->size; + put_word(res, 0); /* Will be overwritten later */ + if(win32) + { + put_word(res, level); + } + put_string(res, val->key, win32 ? str_unicode : str_char, TRUE); + put_pad(res); + tag = res->size; + for(i = 0; i < val->value.words->nwords; i++) + { + put_word(res, val->value.words->words[i]); + } + *(WORD *)&(res->data[valvalsizetag]) = (WORD)(res->size - tag); + *(WORD *)&(res->data[valblksizetag]) = (WORD)(res->size - valblksizetag); + put_pad(res); + } + else if(val->type == val_block) + { + versionblock2res(res, val->value.block, level+1); + } + else + { + internal_error(__FILE__, __LINE__, "Invalid value indicator %d in VERSIONINFO", val->type); + } + } + + /* Set blocksize */ + *(WORD *)&(res->data[blksizetag]) = (WORD)(res->size - blksizetag); +} + +/* + ***************************************************************************** + * Function : versioninfo2res + * Syntax : res_t *versioninfo2res(name_id_t *name, versioninfo_t *ver) + * Input : + * name - Name/ordinal of the resource + * ver - The versioninfo descriptor + * Output : New .res format structure + * Description : + * Remarks : + ***************************************************************************** +*/ +res_t *versioninfo2res(name_id_t *name, versioninfo_t *ver) +{ + int restag; + int rootblocksizetag; + int valsizetag; + int tag; + res_t *res; + string_t vsvi; + ver_block_t *blk; + + assert(name != NULL); + assert(ver != NULL); + + vsvi.type = str_char; + vsvi.str.cstr = "VS_VERSION_INFO"; + vsvi.size = 15; /* Excl. termination */ + + res = new_res(); + restag = put_res_header(res, WRC_RT_VERSION, NULL, name, WRC_MO_MOVEABLE | WRC_MO_PURE, NULL); + rootblocksizetag = res->size; + put_word(res, 0); /* BlockSize filled in later */ + valsizetag = res->size; + put_word(res, 0); /* ValueSize filled in later*/ + if(win32) + put_word(res, 0); /* Tree-level ? */ + put_string(res, &vsvi, win32 ? str_unicode : str_char, TRUE); + if(win32) + put_pad(res); + tag = res->size; + put_dword(res, VS_FFI_SIGNATURE); + put_dword(res, VS_FFI_STRUCVERSION); + put_dword(res, (ver->filever_maj1 << 16) + (ver->filever_maj2 & 0xffff)); + put_dword(res, (ver->filever_min1 << 16) + (ver->filever_min2 & 0xffff)); + put_dword(res, (ver->prodver_maj1 << 16) + (ver->prodver_maj2 & 0xffff)); + put_dword(res, (ver->prodver_min1 << 16) + (ver->prodver_min2 & 0xffff)); + put_dword(res, ver->fileflagsmask); + put_dword(res, ver->fileflags); + put_dword(res, ver->fileos); + put_dword(res, ver->filetype); + put_dword(res, ver->filesubtype); + put_dword(res, 0); /* FileDateMS */ + put_dword(res, 0); /* FileDateLS */ + /* Set ValueSize */ + *(WORD *)&(res->data[valsizetag]) = (WORD)(res->size - tag); + /* Descend into the blocks */ + for(blk = ver->blocks; blk; blk = blk->next) + versionblock2res(res, blk, 0); + /* Set root block's size */ + *(WORD *)&(res->data[rootblocksizetag]) = (WORD)(res->size - rootblocksizetag); + + SetResSize(res, restag); + if(win32) + put_pad(res); + + return res; +} + +/* + ***************************************************************************** + * Function : prep_nid_for_label + * Syntax : char *prep_nid_for_label(name_id_t *nid) + * Input : + * Output : + * Description : Converts a resource name into the first 32 (or less) + * characters of the name with conversions. + * Remarks : + ***************************************************************************** +*/ +#define MAXNAMELEN 32 +char *prep_nid_for_label(name_id_t *nid) +{ + static char buf[MAXNAMELEN+1]; + + assert(nid != NULL); + + if(nid->type == name_str && nid->name.s_name->type == str_unicode) + { + short *sptr; + int i; + sptr = nid->name.s_name->str.wstr; + buf[0] = '\0'; + for(i = 0; *sptr && i < MAXNAMELEN; i++) + { + if((unsigned)*sptr < 0x80 && isprint((char)*sptr)) + buf[i] = *sptr++; + else + warning("Resourcename (str_unicode) contain unprintable characters or invalid translation, ignored"); + } + buf[i] = '\0'; + } + else if(nid->type == name_str && nid->name.s_name->type == str_char) + { + char *cptr; + int i; + cptr = nid->name.s_name->str.cstr; + buf[0] = '\0'; + for(i = 0; *cptr && i < MAXNAMELEN; i++) + { + if((unsigned)*cptr < 0x80 && isprint(*cptr)) + buf[i] = *cptr++; + else + warning("Resourcename (str_char) contain unprintable characters, ignored"); + } + buf[i] = '\0'; + } + else if(nid->type == name_ord) + { + sprintf(buf, "%u", nid->name.i_name); + } + else + { + internal_error(__FILE__, __LINE__, "Resource name_id with invalid type %d", nid->type); + } + return buf; +} +#undef MAXNAMELEN + +/* + ***************************************************************************** + * Function : make_c_name + * Syntax : char *make_c_name(char *base, name_id_t *nid, language_t *lan) + * Input : + * Output : + * Description : Converts a resource name into a valid c-identifier in the + * form "_base_nid". + * Remarks : + ***************************************************************************** +*/ +char *make_c_name(char *base, name_id_t *nid, language_t *lan) +{ + int nlen; + char *buf; + char *ret; + char lanbuf[6]; + + sprintf(lanbuf, "%d", lan ? MAKELANGID(lan->id, lan->sub) : 0); + buf = prep_nid_for_label(nid); + nlen = strlen(buf) + strlen(lanbuf); + nlen += strlen(base) + 4; /* three time '_' and '\0' */ + ret = (char *)xmalloc(nlen); + strcpy(ret, "_"); + strcat(ret, base); + strcat(ret, "_"); + strcat(ret, buf); + strcat(ret, "_"); + strcat(ret, lanbuf); + return ret; +} + +/* + ***************************************************************************** + * Function : get_c_typename + * Syntax : char *get_c_typename(enum res_e type) + * Input : + * Output : + * Description : Convert resource enum to char string to be used in c-name + * creation. + * Remarks : + ***************************************************************************** +*/ +char *get_c_typename(enum res_e type) +{ + switch(type) + { + case res_acc: return "Acc"; + case res_bmp: return "Bmp"; + case res_cur: return "Cur"; + case res_curg: return "CurGrp"; + case res_dlg: + case res_dlgex: return "Dlg"; + case res_fnt: return "Fnt"; + case res_ico: return "Ico"; + case res_icog: return "IcoGrp"; + case res_men: + case res_menex: return "Men"; + case res_rdt: return "RCDat"; + case res_stt: return "StrTab"; + case res_usr: return "Usr"; + case res_msg: return "MsgTab"; + case res_ver: return "VerInf"; + default: return "Oops"; + } +} + +/* + ***************************************************************************** + * Function : resources2res + * Syntax : void resources2res(resource_t *top) + * Input : + * top - The resource-tree to convert + * Output : + * Description : Convert logical resource descriptors into binary data + * Remarks : + ***************************************************************************** +*/ +void resources2res(resource_t *top) +{ + while(top) + { + switch(top->type) + { + case res_acc: + if(!top->binres) + top->binres = accelerator2res(top->name, top->res.acc); + break; + case res_bmp: + if(!top->binres) + top->binres = bitmap2res(top->name, top->res.bmp); + break; + case res_cur: + if(!top->binres) + top->binres = cursor2res(top->res.cur); + break; + case res_curg: + if(!top->binres) + top->binres = cursorgroup2res(top->name, top->res.curg); + break; + case res_dlg: + if(!top->binres) + top->binres = dialog2res(top->name, top->res.dlg); + break; + case res_dlgex: + if(!top->binres) + top->binres = dialogex2res(top->name, top->res.dlgex); + break; + case res_fnt: + if(!top->binres) + top->binres = font2res(top->name, top->res.fnt); + break; + case res_ico: + if(!top->binres) + top->binres = icon2res(top->res.ico); + break; + case res_icog: + if(!top->binres) + top->binres = icongroup2res(top->name, top->res.icog); + break; + case res_men: + if(!top->binres) + top->binres = menu2res(top->name, top->res.men); + break; + case res_menex: + if(!top->binres) + top->binres = menuex2res(top->name, top->res.menex); + break; + case res_rdt: + if(!top->binres) + top->binres = rcdata2res(top->name, top->res.rdt); + break; + case res_stt: + if(!top->binres) + top->binres = stringtable2res(top->res.stt); + break; + case res_usr: + if(!top->binres) + top->binres = user2res(top->name, top->res.usr); + break; + case res_msg: + if(!top->binres) + top->binres = messagetable2res(top->name, top->res.msg); + break; + case res_ver: + if(!top->binres) + top->binres = versioninfo2res(top->name, top->res.ver); + break; + default: + internal_error(__FILE__, __LINE__, "Unknown resource type encountered %d in binary res generation", top->type); + } + top->c_name = make_c_name(get_c_typename(top->type), top->name, top->lan); + top = top->next; + } +} +
diff --git a/tools/wrc/genres.h b/tools/wrc/genres.h new file mode 100644 index 0000000..0f4d9d2 --- /dev/null +++ b/tools/wrc/genres.h
@@ -0,0 +1,25 @@ +/* + * Generate resource prototypes + * + * Copyright 1998 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WRC_GENRES_H +#define __WRC_GENRES_H + +#ifndef __WRC_WRCTYPES_H +#include "wrctypes.h" +#endif + +res_t *new_res(void); +res_t *grow_res(res_t *r, int add); +void put_byte(res_t *res, unsigned c); +void put_word(res_t *res, unsigned w); +void put_dword(res_t *res, unsigned d); +void resources2res(resource_t *top); +char *get_c_typename(enum res_e type); +char *make_c_name(char *base, name_id_t *nid, language_t *lan); +char *prep_nid_for_label(name_id_t *nid); + +#endif
diff --git a/tools/wrc/newstruc.c b/tools/wrc/newstruc.c new file mode 100644 index 0000000..6d33c4b --- /dev/null +++ b/tools/wrc/newstruc.c
@@ -0,0 +1,251 @@ +/* + * Create dynamic new structures of various types + * and some utils in that trend. + * + * Copyright 1998 Bertho A. Stultiens + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include <config.h> +#include "wrc.h" +#include "newstruc.h" +#include "utils.h" +#include "parser.h" + +/* Generate new_* functions that have no parameters (NOTE: no ';') */ +__NEW_STRUCT_FUNC(dialog) +__NEW_STRUCT_FUNC(dialogex) +__NEW_STRUCT_FUNC(name_id) +__NEW_STRUCT_FUNC(menu) +__NEW_STRUCT_FUNC(menuex) +__NEW_STRUCT_FUNC(menu_item) +__NEW_STRUCT_FUNC(menuex_item) +__NEW_STRUCT_FUNC(control) +__NEW_STRUCT_FUNC(icon) +__NEW_STRUCT_FUNC(cursor) +__NEW_STRUCT_FUNC(versioninfo) +__NEW_STRUCT_FUNC(ver_value) +__NEW_STRUCT_FUNC(ver_block) +__NEW_STRUCT_FUNC(stt_entry) +__NEW_STRUCT_FUNC(accelerator) +__NEW_STRUCT_FUNC(event) +__NEW_STRUCT_FUNC(raw_data) +__NEW_STRUCT_FUNC(lvc) +__NEW_STRUCT_FUNC(res_count) +__NEW_STRUCT_FUNC(string) + +/* New instances for all types of structures */ +/* Very inefficient (in size), but very functional :-] + * Especially for type-checking. + */ +resource_t *new_resource(enum res_e t, void *res, int memopt, language_t *lan) +{ + resource_t *r = (resource_t *)xmalloc(sizeof(resource_t)); + r->type = t; + r->res.overlay = res; + r->memopt = memopt; + r->lan = lan; + return r; +} + +version_t *new_version(DWORD v) +{ + version_t *vp = (version_t *)xmalloc(sizeof(version_t)); + *vp = v; + return vp; +} + +characts_t *new_characts(DWORD c) +{ + characts_t *cp = (characts_t *)xmalloc(sizeof(characts_t)); + *cp = c; + return cp; +} + +language_t *new_language(int id, int sub) +{ + language_t *lan = (language_t *)xmalloc(sizeof(language_t)); + lan->id = id; + lan->sub = sub; + return lan; +} + +language_t *dup_language(language_t *l) +{ + if(!l) return NULL; + return new_language(l->id, l->sub); +} + +version_t *dup_version(version_t *v) +{ + if(!v) return NULL; + return new_version(*v); +} + +characts_t *dup_characts(characts_t *c) +{ + if(!c) return NULL; + return new_characts(*c); +} + +rcdata_t *new_rcdata(raw_data_t *rd, int *memopt) +{ + rcdata_t *rc = (rcdata_t *)xmalloc(sizeof(rcdata_t)); + rc->data = rd; + if(memopt) + { + rc->memopt = *memopt; + free(memopt); + } + else + rc->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE; + return rc; +} + +font_id_t *new_font_id(int size, string_t *face, int weight, int italic) +{ + font_id_t *fid = (font_id_t *)xmalloc(sizeof(font_id_t)); + fid->name = face; + fid->size = size; + fid->weight = weight; + fid->italic = italic; + return fid; +} + +user_t *new_user(name_id_t *type, raw_data_t *rd, int *memopt) +{ + user_t *usr = (user_t *)xmalloc(sizeof(user_t)); + usr->data = rd; + if(memopt) + { + usr->memopt = *memopt; + free(memopt); + } + else + usr->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE; + usr->type = type; + return usr; +} + +font_t *new_font(raw_data_t *rd, int *memopt) +{ + font_t *fnt = (font_t *)xmalloc(sizeof(font_t)); + fnt->data = rd; + if(memopt) + { + fnt->memopt = *memopt; + free(memopt); + } + else + fnt->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE; + return fnt; +} + +icon_group_t *new_icon_group(raw_data_t *rd, int *memopt) +{ + icon_group_t *icog = (icon_group_t *)xmalloc(sizeof(icon_group_t)); + if(memopt) + { + icog->memopt = *memopt; + free(memopt); + } + else + icog->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE | WRC_MO_DISCARDABLE; + split_icons(rd, icog, &(icog->nicon)); + free(rd->data); + free(rd); + return icog; +} + +cursor_group_t *new_cursor_group(raw_data_t *rd, int *memopt) +{ + cursor_group_t *curg = (cursor_group_t *)xmalloc(sizeof(cursor_group_t)); + if(memopt) + { + curg->memopt = *memopt; + free(memopt); + } + else + curg->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE | WRC_MO_DISCARDABLE; + split_cursors(rd, curg, &(curg->ncursor)); + free(rd->data); + free(rd); + return curg; +} + +bitmap_t *new_bitmap(raw_data_t *rd, int *memopt) +{ + bitmap_t *bmp = (bitmap_t *)xmalloc(sizeof(bitmap_t)); + bmp->data = rd; + if(memopt) + { + bmp->memopt = *memopt; + free(memopt); + } + else + bmp->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE; + return bmp; +} + +ver_words_t *new_ver_words(int i) +{ + ver_words_t *w = (ver_words_t *)xmalloc(sizeof(ver_words_t)); + w->words = (WORD *)xmalloc(sizeof(WORD)); + w->words[0] = (WORD)i; + w->nwords = 1; + return w; +} + +ver_words_t *add_ver_words(ver_words_t *w, int i) +{ + w->words = (WORD *)xrealloc(w->words, (w->nwords+1) * sizeof(WORD)); + w->words[w->nwords] = (WORD)i; + w->nwords++; + return w; +} + +messagetable_t *new_messagetable(raw_data_t *rd) +{ + messagetable_t *msg = (messagetable_t *)xmalloc(sizeof(messagetable_t)); + msg->data = rd; + return msg; +} + +void copy_raw_data(raw_data_t *dst, raw_data_t *src, int offs, int len) +{ + assert(offs <= src->size); + assert(offs + len <= src->size); + if(!dst->data) + { + dst->data = (char *)xmalloc(len); + dst->size = 0; + } + else + dst->data = (char *)xrealloc(dst->data, dst->size + len); + /* dst->size holds the offset to copy to */ + memcpy(dst->data + dst->size, src->data + offs, len); + dst->size += len; +} + +int *new_int(int i) +{ + int *ip = (int *)xmalloc(sizeof(int)); + *ip = i; + return ip; +} + +stringtable_t *new_stringtable(lvc_t *lvc) +{ + stringtable_t *stt = (stringtable_t *)xmalloc(sizeof(stringtable_t)); + + if(lvc) + stt->lvc = *lvc; + + return stt; +} + +
diff --git a/tools/wrc/newstruc.h b/tools/wrc/newstruc.h new file mode 100644 index 0000000..61d3907 --- /dev/null +++ b/tools/wrc/newstruc.h
@@ -0,0 +1,66 @@ +/* + * Create dynamic new structures of various types + * + * Copyright 1998 Bertho A. Stultiens + * + */ + +#ifndef __WRC_NEWSTRUC_H +#define __WRC_NEWSTRUC_H + +#ifndef __WRC_WRCTYPES_H +#include "wrctypes.h" +#endif + +#define __NEW_STRUCT_FUNC(p) \ + p##_t *new_##p(void)\ + {\ + return (p##_t *)xmalloc(sizeof(p##_t));\ + } + +#define __NEW_STRUCT_PROTO(p) p##_t *new_##p(void) + +__NEW_STRUCT_PROTO(dialog); +__NEW_STRUCT_PROTO(dialogex); +__NEW_STRUCT_PROTO(name_id); +__NEW_STRUCT_PROTO(menu); +__NEW_STRUCT_PROTO(menuex); +__NEW_STRUCT_PROTO(menu_item); +__NEW_STRUCT_PROTO(menuex_item); +__NEW_STRUCT_PROTO(control); +__NEW_STRUCT_PROTO(icon); +__NEW_STRUCT_PROTO(cursor); +__NEW_STRUCT_PROTO(versioninfo); +__NEW_STRUCT_PROTO(ver_value); +__NEW_STRUCT_PROTO(ver_block); +__NEW_STRUCT_PROTO(stt_entry); +__NEW_STRUCT_PROTO(accelerator); +__NEW_STRUCT_PROTO(event); +__NEW_STRUCT_PROTO(raw_data); +__NEW_STRUCT_PROTO(lvc); +__NEW_STRUCT_PROTO(res_count); +__NEW_STRUCT_PROTO(string); + +resource_t *new_resource(enum res_e t, void *res, int memopt, language_t *lan); +version_t *new_version(DWORD v); +characts_t *new_characts(DWORD c); +language_t *new_language(int id, int sub); +language_t *dup_language(language_t *l); +version_t *dup_version(version_t *v); +characts_t *dup_characts(characts_t *c); +rcdata_t *new_rcdata(raw_data_t *rd, int *memopt); +font_id_t *new_font_id(int size, string_t *face, int weight, int italic); +user_t *new_user(name_id_t *type, raw_data_t *rd, int *memopt); +font_t *new_font(raw_data_t *rd, int *memopt); +icon_group_t *new_icon_group(raw_data_t *rd, int *memopt); +cursor_group_t *new_cursor_group(raw_data_t *rd, int *memopt); +bitmap_t *new_bitmap(raw_data_t *rd, int *memopt); +ver_words_t *new_ver_words(int i); +ver_words_t *add_ver_words(ver_words_t *w, int i); +messagetable_t *new_messagetable(raw_data_t *rd); +void copy_raw_data(raw_data_t *dst, raw_data_t *src, int offs, int len); +int *new_int(int i); +stringtable_t *new_stringtable(lvc_t *lvc); + +#endif +
diff --git a/tools/wrc/parser.h b/tools/wrc/parser.h new file mode 100644 index 0000000..a038371 --- /dev/null +++ b/tools/wrc/parser.h
@@ -0,0 +1,32 @@ +/* + * Copyright 1998 Bertho A. Stultiens (BS) + * + * Shared things between parser.l and parser.y and some others + */ + +#ifndef __WRC_PARSER_H +#define __WRC_PARSER_H + +/* From parser.y */ +extern int yydebug; +extern int indialog; /* Set when parsing dialogs */ +extern int want_rscname; /* Set when a resource's name is required */ + +int yyparse(void); +void split_icons(raw_data_t *rd, icon_group_t *icog, int *nico); +void split_cursors(raw_data_t *rd, cursor_group_t *curg, int *ncur); + +/* From parser.l */ +extern FILE *yyin; +extern int line_number; + +int yylex(void); +void set_yywf(void); +void set_pp_ignore(int state); +void push_to(int start); +void pop_start(void); +void strip_til_semicolon(void); +void strip_til_parenthesis(void); + +#endif +
diff --git a/tools/wrc/parser.l b/tools/wrc/parser.l new file mode 100644 index 0000000..7b411d8 --- /dev/null +++ b/tools/wrc/parser.l
@@ -0,0 +1,862 @@ +/* -*-C-*- + * + * Copyright 1994 Martin von Loewis + * Copyright 1998 Bertho A. Stultiens (BS) + * + * 19-May-1998 BS - Started to build a preprocessor. + * - Changed keyword processing completely to + * table-lookups. + * + * 20-Apr-1998 BS - Added ';' comment stripping + * + * 17-Apr-1998 BS - Made the win32 keywords optional when compiling in + * 16bit mode + * + * 15-Apr-1998 BS - Changed string handling to include escapes + * - Added unicode string handling (no codepage + * translation though). + * - 'Borrowed' the main idea of string scanning from + * the flex manual pages. + * - Added conditional handling of scanning depending + * on the state of the parser. This was mainly required + * to distinguish a file to load or raw data that + * follows. MS's definition of filenames is rather + * complex... It can be unquoted or double quoted. If + * double quoted, then the '\\' char is not automatically + * escaped according to Borland's rc compiler, but it + * accepts both "\\path\\file.rc" and "\path\file.rc". + * This makes life very hard! I go for the escaped + * version, as this seems to be the documented way... + * - Single quoted strings are now parsed and converted + * here. + * - Added comment stripping. The implementation is + * 'borrowed' from the flex manpages. + * - Rebuild string processing so that it may contain + * escaped '\0'. + */ + +/* Exclusive rules when looking for a filename */ +%x yywf +/* Exclusive string handling */ +%x yystr +/* Exclusive unicode string handling */ +%x yylstr +/* Exclusive rcdata single quoted data handling */ +%x yyrcd +/* Exclusive comment eating... */ +%x comment +/* Preprocessor exclusives */ +%x pp_incl +%x pp_def +%x pp_undef +%x pp_if +%x pp_ifdef +%x pp_ifndef +%x pp_elif +%x pp_else +%x pp_endif +%x pp_error +/* Set when accumulating #define's expansion text */ +%x pp_def_s +/* Set when processing function type defines */ +%x pp_ignore +/* Set when need to strip to eol */ +%x pp_ignore_eol +/* Set when handling a false #if case */ +%x pp_false +/* Set when stripping c-junk */ +%x pp_strips +%x pp_stripp +%x pp_stripp_final + +/*%option stack*/ +%option never-interactive +/*%option noyywrap */ +/* Some shortcut definitions */ +ws [ \f\t\r] +cident [a-zA-Z_][0-9a-zA-Z_]* + +%{ + +/*#define LEX_DEBUG*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <config.h> +#include "wrc.h" +#include "utils.h" +#include "preproc.h" +#include "parser.h" +#include "newstruc.h" + +#include "y.tab.h" + +#define YY_USE_PROTOS +#define YY_NO_UNPUT + +raw_data_t *new_raw_data(void); + +void addcchar(char c); +void addwchar(short s); +string_t *get_buffered_cstring(void); +string_t *get_buffered_wstring(void); +string_t *make_string(char *s); + +int line_number = 1; +static char cbuffer[1024]; /* Buffers for string collection */ +static int cbufidx; +static short wbuffer[1024]; +static int wbufidx; +static int want_nl = 0; /* Set when newline needs to go to parser */ +static int want_ident = 0; /* Set is #ifdef, #ifndef or defined is seen */ +static int stripslevel = 0; /* Count {} during pp_strips mode */ +static int stripplevel = 0; /* Count () during pp_strips mode */ +static char *substtext = NULL; /* Holds the substition text while getting a define */ +static int cjunk_tagline; /* Where did we start stripping (helps error tracking) */ + +#ifdef YY_USE_STACK +void push_to(int start) { yy_push_state(start); } +void pop_start(void) { yy_pop_state(start); } +#else +#define MAXSTARTSTACK 32 +static int startstack[MAXSTARTSTACK]; +static int startstackidx = 0; + +void push_to(int start) +{ + if(yydebug) + printf("push_to(%d): %d -> %d\n", line_number, YY_START, start); + if(startstackidx >= MAXSTARTSTACK-1) + internal_error(__FILE__, __LINE__, "Start condition stack overflow"); + startstack[startstackidx++] = YY_START; + BEGIN(start); +} + +void pop_start(void) +{ + if(yydebug) + printf("pop_start(%d): %d <- %d\n", line_number, startstack[startstackidx-1], YY_START); + if(startstackidx <= 0) + internal_error(__FILE__, __LINE__, "Start condition stack underflow"); + --startstackidx; + BEGIN(startstack[startstackidx]); +} +#endif + + +struct bufferstackentry { + YY_BUFFER_STATE bufferstate; /* Buffer to switch back to */ + struct pp_entry *define; /* Points to expanding define + or NULL if handling includes + */ + int line_number; /* Line that we were handling */ + char *filename; /* Filename that we were handling */ +}; + +#define MAXBUFFERSTACK 128 +static struct bufferstackentry bufferstack[MAXBUFFERSTACK]; +static int bufferstackidx = 0; + +void push_buffer(YY_BUFFER_STATE buf, struct pp_entry *ppp, char *filename) +{ + if(yydebug) + printf("push_buffer: %p %p %p\n", buf, ppp, filename); + if(bufferstackidx >= MAXBUFFERSTACK-1) + internal_error(__FILE__, __LINE__, "Buffer stack overflow"); + memset(&bufferstack[bufferstackidx], 0, sizeof(bufferstack[0])); + bufferstack[bufferstackidx].bufferstate = buf; + bufferstack[bufferstackidx].define = ppp; + if(ppp) + ppp->expanding = 1; + else if(filename) + { + /* These will track the yyerror to the correct file and line */ + bufferstack[bufferstackidx].line_number = line_number; + line_number = 1; + bufferstack[bufferstackidx].filename = input_name; + input_name = filename; + } + else + internal_error(__FILE__, __LINE__, "Pushing buffer without knowing where to go to"); + bufferstackidx++; +} + +YY_BUFFER_STATE pop_buffer(void) +{ + if(bufferstackidx <= 0) + return (YY_BUFFER_STATE)0; + bufferstackidx--; + if(bufferstack[bufferstackidx].define) + bufferstack[bufferstackidx].define->expanding = 0; + else + { + line_number = bufferstack[bufferstackidx].line_number; + input_name = bufferstack[bufferstackidx].filename; + fclose(yyin); + } + if(yydebug) + printf("pop_buffer: %p %p (%d) %p\n", + bufferstack[bufferstackidx].bufferstate, + bufferstack[bufferstackidx].define, + bufferstack[bufferstackidx].line_number, + bufferstack[bufferstackidx].filename); + yy_switch_to_buffer(bufferstack[bufferstackidx].bufferstate); + return bufferstack[bufferstackidx].bufferstate; +} + +void do_include(char *name, int namelen) +{ + char *cpy = (char *)xmalloc(namelen); + strcpy(cpy, name+1); /* strip leading " or < */ + cpy[namelen-2] = '\0'; /* strip trailing " or > */ + if((yyin = open_include(cpy, name[0] == '"')) == NULL) + yyerror("Unable to open include file %s", cpy); + push_buffer(YY_CURRENT_BUFFER, NULL, cpy); + yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); +} + + +struct keyword { + char *keyword; + int token; + int isextension; + int needcase; + int alwayskeyword; +}; + +static struct keyword keywords[] = { + { "ACCELERATORS", ACCELERATORS, 0, 0, 0}, + { "ALT", ALT, 0, 0, 0}, + { "ASCII", ASCII, 0, 0, 0}, + { "AUTO3STATE", AUTO3STATE, 1, 0, 0}, + { "AUTOCHECKBOX", AUTOCHECKBOX, 1, 0, 0}, + { "AUTORADIOBUTTON", AUTORADIOBUTTON, 1, 0, 0}, + { "BEGIN", tBEGIN, 0, 0, 1}, + { "BITMAP", tBITMAP, 0, 0, 0}, + { "BLOCK", BLOCK, 0, 0, 1}, + { "CAPTION", CAPTION, 0, 0, 0}, + { "CHARACTERISTICS", CHARACTERISTICS, 1, 0, 0}, + { "CHECKBOX", CHECKBOX, 0, 0, 0}, + { "CHECKED", CHECKED, 0, 0, 0}, + { "CLASS", CLASS, 0, 0, 0}, + { "COMBOBOX", COMBOBOX, 0, 0, 0}, + { "CONTROL", CONTROL, 0, 0, 0}, + { "CTEXT", CTEXT, 0, 0, 0}, + { "CURSOR", CURSOR, 0, 0, 0}, + { "defined", tDEFINED, 0, 1, 1}, + { "DEFPUSHBUTTON", DEFPUSHBUTTON, 0, 0, 1}, + { "DIALOG", DIALOG, 0, 0, 0}, + { "DIALOGEX", DIALOGEX, 1, 0, 0}, + { "DISCARDABLE", DISCARDABLE, 0, 0, 0}, + { "EDITTEXT", EDITTEXT, 0, 0, 0}, + { "END", tEND, 0, 0, 1}, + { "EXSTYLE", EXSTYLE, 0, 0, 0}, + { "extern", tEXTERN, 0, 1, 1}, + { "FILEFLAGS", FILEFLAGS, 0, 0, 0}, + { "FILEFLAGSMASK", FILEFLAGSMASK, 0, 0, 0}, + { "FILEOS", FILEOS, 0, 0, 0}, + { "FILESUBTYPE", FILESUBTYPE, 0, 0, 0}, + { "FILETYPE", FILETYPE, 0, 0, 0}, + { "FILEVERSION", FILEVERSION, 0, 0, 0}, + { "FIXED", tFIXED, 0, 0, 0}, + { "FONT", FONT, 0, 0, 0}, + { "GRAYED", GRAYED, 0, 0, 0}, + { "GROUPBOX", GROUPBOX, 0, 0, 0}, + { "HELP", HELP, 0, 0, 0}, + { "ICON", ICON, 0, 0, 0}, + { "IMPURE", IMPURE, 0, 0, 0}, + { "INACTIVE", INACTIVE, 0, 0, 0}, + { "LANGUAGE", LANGUAGE, 1, 0, 1}, + { "LISTBOX", LISTBOX, 0, 0, 0}, + { "LOADONCALL", LOADONCALL, 0, 0, 0}, + { "LTEXT", LTEXT, 0, 0, 0}, + { "MENU", MENU, 0, 0, 0}, + { "MENUBARBREAK", MENUBARBREAK, 0, 0, 0}, + { "MENUBREAK", MENUBREAK, 0, 0, 0}, + { "MENUEX", MENUEX, 1, 0, 0}, + { "MENUITEM", MENUITEM, 0, 0, 0}, + { "MESSAGETABLE", MESSAGETABLE, 1, 0, 0}, + { "MOVEABLE", MOVEABLE, 0, 0, 0}, + { "NOINVERT", NOINVERT, 0, 0, 0}, + { "NOT", NOT, 0, 0, 0}, + { "POPUP", POPUP, 0, 0, 0}, + { "PRELOAD", PRELOAD, 0, 0, 0}, + { "PRODUCTVERSION", PRODUCTVERSION, 0, 0, 0}, + { "PURE", tPURE, 0, 0, 0}, + { "PUSHBUTTON", PUSHBUTTON, 0, 0, 0}, + { "RADIOBUTTON", RADIOBUTTON, 0, 0, 0}, + { "RCDATA", RCDATA, 0, 0, 0}, + { "RTEXT", RTEXT, 0, 0, 0}, + { "SCROLLBAR", SCROLLBAR, 0, 0, 0}, + { "SEPARATOR", SEPARATOR, 0, 0, 0}, + { "SHIFT", SHIFT, 0, 0, 0}, + { "STATE3", STATE3, 1, 0, 0}, + { "STRING", tSTRING, 0, 0, 0}, + { "STRINGTABLE", STRINGTABLE, 0, 0, 1}, + { "STYLE", STYLE, 0, 0, 0}, + { "typedef", tTYPEDEF, 0, 1, 1}, + { "VALUE", VALUE, 0, 0, 0}, + { "VERSION", VERSION, 1, 0, 0}, + { "VERSIONINFO", VERSIONINFO, 0, 0, 0}, + { "VIRTKEY", VIRTKEY, 0, 0, 0} +}; + +#define NKEYWORDS (sizeof(keywords)/sizeof(keywords[0])) +#define KWP(p) ((struct keyword *)(p)) +int kw_cmp_func(const void *s1, const void *s2) +{ + int ret; + ret = stricmp(KWP(s1)->keyword, KWP(s2)->keyword); + if(!ret && (KWP(s1)->needcase || KWP(s2)->needcase)) + return strcmp(KWP(s1)->keyword, KWP(s2)->keyword); + else + return ret; +} + +#define KW_BSEARCH +#define DO_SORT +struct keyword *iskeyword(char *kw) +{ + struct keyword *kwp; + struct keyword key; + key.keyword = kw; + key.needcase = 0; +#ifdef DO_SORT + { + /* Make sure that it is sorted for bsearsh */ + static int sorted = 0; + if(!sorted) + { + qsort(keywords, NKEYWORDS, sizeof(keywords[0]), kw_cmp_func); + sorted = 1; + } + } +#endif +#ifdef KW_BSEARCH + kwp = bsearch(&key, keywords, NKEYWORDS, sizeof(keywords[0]), kw_cmp_func); +#else + { + int i; + for(i = 0; i < NKEYWORDS; i++) + { + if(!kw_cmp_func(&key, &keywords[i])) + break; + } + if(i < NKEYWORDS) + kwp = &keywords[i]; + else + kwp = NULL; + } +#endif + +#ifdef LEX_DEBUG + if(kwp && !strcmp(kwp->keyword, "LANGUAGE")) + printf("Got Language\n"); +#endif + if(kwp == NULL || (kwp->isextension && !extensions)) + return NULL; + else + return kwp; +} + +void add_to_substtext(char *text, int len) +{ + if(!substtext) + { + substtext = xstrdup(text); + } + else + { + substtext = (char *)xrealloc(substtext, strlen(substtext)+len+1); + strcat(substtext, text); + } +} + +%} + +%% + /* #include handling */ +^{ws}*#{ws}*include{ws}* push_to(pp_incl); +<pp_incl>\<[^\n\>]+\> do_include(yytext, yyleng); pop_start(); +<pp_incl>\"[^\n\>]+\" do_include(yytext, yyleng); pop_start(); +<pp_incl>. yyerror("Malformed #include"); + + /* #define handling */ +^{ws}*#{ws}*define{ws}* push_to(pp_def); +<pp_def>{cident} { + set_define(yytext); + push_to(pp_def_s); + } +<pp_def>{cident}\( push_to(pp_ignore); /* Ignore function-like defines for now*/ +<pp_def>. yyerror("Malformed #define"); + +<pp_ignore,pp_def_s>[^\/\\\n]* { + if(YY_START == pp_def_s) + add_to_substtext(yytext, yyleng); + } +<pp_ignore,pp_def_s>\/[^\/\*][^\/\\\n]* { /* Comment is handled in normal handling */ + if(YY_START == pp_def_s) + add_to_substtext(yytext, yyleng); + } +<pp_ignore,pp_def_s>\\{ws}*\n line_number++; /* Line continuation */ +<pp_ignore,pp_def_s>\n { + if(YY_START == pp_def_s) + { + add_define(substtext ? substtext : ""); + free(substtext); + substtext = NULL; + } + line_number++; + pop_start(); + pop_start(); + } + + /* #undef handling */ +^{ws}*#{ws}*undef{ws}* push_to(pp_undef); +<pp_undef>{cident} { + del_define(yytext); + pop_start(); + /*push_to(pp_ignore);*/ + } + + /* Conditional handling */ +<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*if{ws}* { + if(YY_START == pp_false) + { + if(yydebug) + printf("(%d)#if ignored\n", line_number); + push_if(0, 0, 1); + push_to(pp_ignore_eol); + } + else + { + push_to(INITIAL); + want_nl = 1; + return tIF; + } + } +<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*ifdef{ws}* { + if(YY_START == pp_false) + { + if(yydebug) + printf("(%d)#ifdef ignored\n", line_number); + push_if(0, 0, 1); + push_to(pp_ignore_eol); + } + else + { + push_to(INITIAL); + want_nl = 1; + want_ident = 1; + return tIFDEF; + } + } +<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*ifndef{ws}* { + if(YY_START == pp_false) + { + if(yydebug) + printf("(%d)#ifndef ignored\n", line_number); + push_if(0, 0, 1); + push_to(pp_ignore_eol); + } + else + { + push_to(INITIAL); + want_nl = 1; + want_ident = 1; + return tIFNDEF; + } + } +<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*elif{ws}* { + if(!isnevertrue_if()) + { + push_to(INITIAL); + want_nl = 1; + return tELIF; + } + else if(YY_START == pp_false) + push_to(pp_ignore_eol); + if(yydebug) + printf("(%d)#elif ignored\n", line_number); + } +<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*else{ws}* { + if(!isnevertrue_if()) + { + push_to(INITIAL); + want_nl = 1; + return tELSE; + } + if(yydebug) + printf("(%d)#else ignored\n", line_number); + } +<INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*endif{ws}* { + if(!isnevertrue_if()) + { + want_nl = 1; + return tENDIF; + } + else + { + if(yydebug) + printf("(%d)#endif ignored\n", line_number); + pop_if(); + } + } + + /* The error directive */ +^{ws}*#{ws}*error{ws}* push_to(pp_error); +<pp_error>[^\n]* yyerror("Error directive: %s", yytext); + + /* preprocessor junk */ +^{ws}*#{ws}*pragma[^\n]* ; /* Ignore #pragma */ +^{ws}*#{ws}*line[^\n]* ; /* Ignore #line */ + /* We'll get an error on malformed #xxx statements + * by not recognising '#' at all. This helps tracking + * preprocessor errors. + */ + /*^{ws}*#{ws}* ; Ignore # */ + +<pp_strips>\{ stripslevel++; +<pp_strips>\} stripslevel--; +<pp_strips>; if(!stripslevel) pop_start(); +<pp_strips>[^\{\};\n#]* ; /* Ignore rest */ + +<pp_stripp>\( stripplevel++; +<pp_stripp>\) { + stripplevel--; + if(!stripplevel) + { + pop_start(); + push_to(pp_stripp_final); + } + } +<pp_stripp>[^\(\);\n#]* ; /* Ignore rest */ + +<pp_stripp_final>{ws}* ; /* Ignore */ +<pp_stripp_final>; pop_start(); /* Kill the semicolon */ +<pp_stripp_final>\n line_number++; pop_start(); +<pp_stripp_final>. yyless(0); pop_start(); + +<pp_false>. ; /* Ignore everything except #xxx during false #if state */ + +<pp_ignore_eol>[^\n]* pop_start(); + + /* These are special cases due to filename scanning */ +<yywf>[Dd][Ii][Ss][Cc][Aa][Rr][Dd][Aa][Bb][Ll][Ee] return DISCARDABLE; +<yywf>[Ff][Ii][Xx][Ee][Dd] return tFIXED; +<yywf>[Ii][Mm][Pp][Uu][Rr][Ee] return IMPURE; +<yywf>[Mm][Oo][Vv][Ee][Aa][Bb][Ll][Ee] return MOVEABLE; +<yywf>[Ll][Oo][Aa][Dd][Oo][Nn][Cc][Aa][Ll][Ll] return LOADONCALL; +<yywf>[Pp][Rr][Ee][Ll][Oo][Aa][Dd] return PRELOAD; +<yywf>[Pp][Uu][Rr][Ee] return tPURE; + +\{ return tBEGIN; +\} return tEND; + +[0-9]+[lL]? { yylval.num = atoi(yytext); return NUMBER; } +0[xX][0-9A-Fa-f]+[lL]? { yylval.num = strtoul(yytext,0,16); return NUMBER; } +0[oO][0-7]+ { yylval.num = strtoul(yytext+2,0,8); return NUMBER; } +[A-Za-z_0-9]+ { + struct keyword *token; + struct pp_entry *ppp; + + want_rscname = 0; + + if(want_ident) + { + /* Prevent preprocessor subst */ + want_ident = 0; + yylval.str = make_string(yytext); + #ifdef LEX_DEBUG + printf("want IDENT (%s, %d): <%s>\n", input_name, line_number, yytext); + #endif + return IDENT; + } + else if((ppp = pp_lookup(yytext)) != NULL) + { + /* Do preprocessor substitution, + * but expand only if macro is not + * already expanding. + */ + if(!ppp->expanding) + { + #ifdef LEX_DEBUG + printf("expand IDENT (%s, %d): <%s>\n", input_name, line_number, yytext); + #endif + push_buffer(YY_CURRENT_BUFFER, ppp, NULL); + yy_scan_string(ppp->subst); + } + } + else if((token = iskeyword(yytext)) != NULL + && !(!token->alwayskeyword && want_rscname)) + { + switch(token->token) + { + case tDEFINED: + want_ident = 1; + break; + /*case RCDATA:*/ + case CURSOR: + case tBITMAP: + case MESSAGETABLE: + push_to(yywf); + break; + case FONT: + case ICON: + if(!indialog) + push_to(yywf); + break; + case DIALOG: + case DIALOGEX: + indialog = 1; + break; + } + return token->token; + } + else + { + yylval.str = make_string(yytext); + #ifdef LEX_DEBUG + printf("%s IDENT (%s, %d): <%s>\n", + want_rscname ? "rscname" : "just", + input_name, + line_number, + yytext); + #endif + return IDENT; + } + } +\|\| return LOGOR; +\&\& return LOGAND; +\=\= return EQ; +\!\= return NE; +\<\= return LTE; +\>\= return GTE; + +<yywf>[^ \f\t\r\n]* { pop_start(); yylval.str = make_string(yytext); return FILENAME; } +<yywf>\"[^\"]*\" { pop_start(); yylval.str = make_string(yytext); return FILENAME; } + +L\" { + push_to(yylstr); + wbufidx = 0; + if(!win32) + yywarning("16bit resource contains unicode strings\n"); + } +<yylstr>\" { + pop_start(); + yylval.str = get_buffered_wstring(); + return tSTRING; + } +<yylstr>\n yyerror("Unterminated string"); +<yylstr>\\[0-7]{1,6} { /* octal escape sequence */ + int result; + result = strtol(yytext+1, 0, 8); + if ( result > 0xffff ) + yyerror("Character constant out of range"); + addwchar((short)result); + } +<yylstr>\\x[0-9a-fA-F]{4} { /* hex escape sequence */ + int result; + result = strtol(yytext+2, 0, 16); + addwchar((short)result); + } +<yylstr>\\[0-9]+ yyerror("Bad escape secuence"); +<yylstr>\\a addwchar('\a'); +<yylstr>\\b addwchar('\b'); +<yylstr>\\f addwchar('\f'); +<yylstr>\\n addwchar('\n'); +<yylstr>\\r addwchar('\r'); +<yylstr>\\t addwchar('\t'); +<yylstr>\\v addwchar('\v'); +<yylstr>\\(.|\n) addwchar(yytext[1]); +<yylstr>[^\\\n\"]+ { + char *yptr = yytext; + while(*yptr) /* FIXME: codepage translation */ + addwchar(*yptr++ & 0xff); + } + +\" { + push_to(yystr); + cbufidx = 0; + } +<yystr>\" { + pop_start(); + yylval.str = get_buffered_cstring(); + return tSTRING; + } +<yystr>\n yyerror("Unterminated string"); +<yystr>\\[0-7]{1,3} { /* octal escape sequence */ + int result; + result = strtol(yytext+1, 0, 8); + if ( result > 0xff ) + yyerror("Character constant out of range"); + addcchar((char)result); + } +<yystr>\\x[0-9a-fA-F]{2} { /* hex escape sequence */ + int result; + result = strtol(yytext+2, 0, 16); + addcchar((char)result); + } +<yystr>\\[0-9]+ yyerror("Bad escape secuence"); +<yystr>\\a addcchar('\a'); +<yystr>\\b addcchar('\b'); +<yystr>\\f addcchar('\f'); +<yystr>\\n addcchar('\n'); +<yystr>\\r addcchar('\r'); +<yystr>\\t addcchar('\t'); +<yystr>\\v addcchar('\v'); +<yystr>\\(.|\n) addcchar(yytext[1]); +<yystr>[^\\\n\"]+ { + char *yptr = yytext; + while(*yptr) + addcchar(*yptr++); + } + + + +\' { + push_to(yyrcd); + cbufidx = 0; + } +<yyrcd>\' { + pop_start(); + yylval.raw = new_raw_data(); + yylval.raw->size = cbufidx; + yylval.raw->data = xmalloc(yylval.raw->size); + memcpy(yylval.raw->data, cbuffer, yylval.raw->size); + return RAWDATA; + } +<yyrcd>[0-9a-fA-F]{2} { + int result; + result = strtol(yytext, 0, 16); + addcchar((char)result); + } +<yyrcd>{ws}+ ; /* Ignore space */ +<yyrcd>. yyerror("Malformed data-line"); + +<INITIAL,pp_ignore,pp_def_s>"/*" push_to(comment); /* Eat comment */ +<comment>[^*\n]* ; +<comment>"*"+[^*/\n]* ; +<comment>\n line_number++; +<comment>"*"+"/" pop_start(); + +;[^\n]* ; /* Eat comment */ +<INITIAL,pp_ignore,pp_def_s>"//"[^\n]* ; /* Eat comment */ + +<INITIAL,yywf,pp_false,pp_strips,pp_stripp>\n { + if(YY_START == yywf) + pop_start(); + line_number++; + if(want_nl) + { + want_nl = 0; + return tNL; + } + } +<INITIAL,yywf>{ws}+ ; /* Eat whitespace */ + +<INITIAL>. return yytext[0]; +<<EOF>> { + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + if(!pop_buffer()) + if(YY_START == pp_strips || YY_START == pp_stripp || YY_START == pp_stripp_final) + yyerror("Unexpected end of file during c-junk scanning (started at %d)", cjunk_tagline); + else + yyterminate(); + yy_delete_buffer(b); + } +%% + +#ifndef yywrap +int yywrap(void) +{ +// if(bufferstackidx > 0) +// { +// return 0; +// } + return 1; +} +#endif + +/* These dup functions copy the enclosed '\0' from + * the resource string. + */ +void addcchar(char c) +{ + if(cbufidx >= sizeof(cbuffer)) + internal_error(__FILE__, __LINE__, "Character buffer overflow"); + cbuffer[cbufidx++] = c; +} + +void addwchar(short s) +{ + if(wbufidx >= sizeof(wbuffer)) + internal_error(__FILE__, __LINE__, "Wide character buffer overflow"); + wbuffer[wbufidx++] = (short)(s & 0xff); +} + +string_t *get_buffered_cstring(void) +{ + string_t *str = new_string(); + str->size = cbufidx; + str->type = str_char; + str->str.cstr = (char *)xmalloc(cbufidx+1); + memcpy(str->str.cstr, cbuffer, cbufidx); + str->str.cstr[cbufidx] = '\0'; +/* printf("got cstring \"%s\"\n", str->str.cstr); */ + return str; +} + +string_t *get_buffered_wstring(void) +{ + string_t *str = new_string(); + str->size = wbufidx; + str->type = str_unicode; + str->str.wstr = (short *)xmalloc(2*(wbufidx+1)); + memcpy(str->str.wstr, wbuffer, wbufidx); + str->str.wstr[wbufidx] = 0; + return str; +} + +string_t *make_string(char *s) +{ + string_t *str = new_string(); + str->size = strlen(s); + str->type = str_char; + str->str.cstr = (char *)xmalloc(str->size+1); + memcpy(str->str.cstr, s, str->size+1); + return str; +} + +/* Called from the parser to signal filename request */ +void set_yywf(void) +{ + push_to(yywf); +} + +/* Called from the parser to signal preprocessor if case */ +void set_pp_ignore(int state) +{ + if(state) + push_to(pp_false); + else + pop_start(); +} + +/* Called from the parser to kill c-junk */ +void strip_til_semicolon(void) +{ + cjunk_tagline = line_number; + push_to(pp_strips); +} + +void strip_til_parenthesis(void) +{ + cjunk_tagline = line_number; + stripplevel = 1; /* One scanned already */ + push_to(pp_stripp); +} + +
diff --git a/tools/wrc/parser.y b/tools/wrc/parser.y new file mode 100644 index 0000000..6186da9 --- /dev/null +++ b/tools/wrc/parser.y
@@ -0,0 +1,2264 @@ +%{ +/* + * Copyright Martin von Loewis, 1994 + * Copyright 1998 Bertho A. Stultiens (BS) + * + * 25-May-1998 BS - Found out that I need to support language, version + * and characteristics in inline resources (bitmap, + * cursor, etc) but they can also be specified with + * a filename. This renders my filename-scanning scheme + * worthless. Need to build newline parsing to solve + * this one. + * It will come with version 1.1.0 (sigh). + * + * 19-May-1998 BS - Started to build a builtin preprocessor + * + * 30-Apr-1998 BS - Redid the stringtable parsing/handling. My previous + * ideas had some serious flaws. + * + * 27-Apr-1998 BS - Removed a lot of dead comments and put it in a doc + * file. + * + * 21-Apr-1998 BS - Added correct behavior for cursors and icons. + * - This file is growing too big. It is time to strip + * things and put it in a support file. + * + * 19-Apr-1998 BS - Tagged the stringtable resource so that only one + * resource will be created. This because the table + * has a different layout than other resources. The + * table has to be sorted, and divided into smaller + * resource entries (see comment in source). + * + * 17-Apr-1998 BS - Almost all strings, including identifiers, are parsed + * as string_t which include unicode strings upon + * input. + * - Parser now emits a warning when compiling win32 + * extensions in win16 mode. + * + * 16-Apr-1998 BS - Raw data elements are now *optionally* seperated + * by commas. Read the comments in file sq2dq.l. + * - FIXME: there are instances in the source that rely + * on the fact that int==32bit and pointers are int size. + * - Fixed the conflict in menuex by changing a rule + * back into right recursion. See note in source. + * - UserType resources cannot have an expression as its + * typeclass. See note in source. + * + * 15-Apr-1998 BS - Changed all right recursion into left recursion to + * get reduction of the parsestack. + * This also helps communication between bison and flex. + * Main advantage is that the Empty rule gets reduced + * first, which is used to allocate/link things. + * It also added a shift/reduce conflict in the menuex + * handling, due to expression/option possibility, + * although not serious. + * + * 14-Apr-1998 BS - Redone almost the entire parser. We're not talking + * about making it more efficient, but readable (for me) + * and slightly easier to expand/change. + * This is done primarily by using more reduce states + * with many (intuitive) types for the various resource + * statements. + * - Added expression handling for all resources where a + * number is accepted (not only for win32). Also added + * multiply and division (not MS compatible, but handy). + * Unary minus introduced a shift/reduce conflict, but + * it is not serious. + * + * 13-Apr-1998 BS - Reordered a lot of things + * - Made the source more readable + * - Added Win32 resource definitions + * - Corrected syntax problems with an old yacc (;) + * - Added extra comment about grammar + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <assert.h> +#include <ctype.h> + +#include <config.h> +#include "wrc.h" +#include "utils.h" +#include "newstruc.h" +#include "dumpres.h" +#include "preproc.h" +#include "parser.h" + +#ifdef __BORLANDC__ +#pragma warn -sig +#endif + +DWORD andmask; /* Used to parse 'NOT NUMBER' expressions */ +int indialog = 0; /* Signal flex that we're parsing a dialog */ +int want_rscname = 0; /* Set when a resource's name is required */ +stringtable_t *tagstt; /* Stringtable tag. + * It is set while parsing a stringtable to one of + * the stringtables in the sttres list or a new one + * if the language was not parsed before. + */ +stringtable_t *sttres; /* Stringtable resources. This holds the list of + * stringtables with different lanuages + */ +/* Set to the current options of the currently scanning stringtable */ +static int *tagstt_memopt; +static characts_t *tagstt_characts; +static version_t *tagstt_version; + +/* Prototypes of here defined functions */ +void split_cursors(raw_data_t *rd, cursor_group_t *curg, int *ncur); +void split_icons(raw_data_t *rd, icon_group_t *icog, int *nico); +int alloc_cursor_id(void); +int alloc_icon_id(void); +void ins_stt_entry(stt_entry_t *ste); +int check_stt_entry(stringtable_t *tabs, stt_entry_t *ste); +event_t *get_event_head(event_t *p); +control_t *get_control_head(control_t *p); +ver_value_t *get_ver_value_head(ver_value_t *p); +ver_block_t *get_ver_block_head(ver_block_t *p); +resource_t *get_resource_head(resource_t *p); +menuex_item_t *get_itemex_head(menuex_item_t *p); +menu_item_t *get_item_head(menu_item_t *p); +raw_data_t *merge_raw_data_str(raw_data_t *r1, string_t *str); +raw_data_t *merge_raw_data_int(raw_data_t *r1, int i); +raw_data_t *merge_raw_data(raw_data_t *r1, raw_data_t *r2); +raw_data_t *str2raw_data(string_t *str); +raw_data_t *int2raw_data(int i); +raw_data_t *load_file(string_t *name); +itemex_opt_t *new_itemex_opt(int id, int type, int state, int helpid); +event_t *add_string_event(string_t *key, int id, int flags, event_t *prev); +event_t *add_event(int key, int id, int flags, event_t *prev); +dialogex_t *dialogex_version(version_t *v, dialogex_t *dlg); +dialogex_t *dialogex_characteristics(characts_t *c, dialogex_t *dlg); +dialogex_t *dialogex_language(language_t *l, dialogex_t *dlg); +dialogex_t *dialogex_menu(name_id_t *m, dialogex_t *dlg); +dialogex_t *dialogex_class(name_id_t *n, dialogex_t *dlg); +dialogex_t *dialogex_font(font_id_t *f, dialogex_t *dlg); +dialogex_t *dialogex_caption(string_t *s, dialogex_t *dlg); +dialogex_t *dialogex_exstyle(int st, dialogex_t *dlg); +dialogex_t *dialogex_style(int st, dialogex_t *dlg); +name_id_t *convert_ctlclass(name_id_t *cls); +control_t *ins_ctrl(int type, int style, control_t *ctrl, control_t *prev); +dialog_t *dialog_version(version_t *v, dialog_t *dlg); +dialog_t *dialog_characteristics(characts_t *c, dialog_t *dlg); +dialog_t *dialog_language(language_t *l, dialog_t *dlg); +dialog_t *dialog_menu(name_id_t *m, dialog_t *dlg); +dialog_t *dialog_class(name_id_t *n, dialog_t *dlg); +dialog_t *dialog_font(font_id_t *f, dialog_t *dlg); +dialog_t *dialog_caption(string_t *s, dialog_t *dlg); +dialog_t *dialog_exstyle(int st, dialog_t *dlg); +dialog_t *dialog_style(int st, dialog_t *dlg); +resource_t *build_stt_resources(stringtable_t *stthead); +stringtable_t *find_stringtable(lvc_t *lvc); + +%} +%union{ + string_t *str; + int num; + int *iptr; + resource_t *res; + accelerator_t *acc; + bitmap_t *bmp; + cursor_t *cur; + cursor_group_t *curg; + dialog_t *dlg; + dialogex_t *dlgex; + font_t *fnt; + icon_t *ico; + icon_group_t *icog; + menu_t *men; + menuex_t *menex; + rcdata_t *rdt; + stringtable_t *stt; + stt_entry_t *stte; + user_t *usr; + messagetable_t *msg; + versioninfo_t *veri; + control_t *ctl; + name_id_t *nid; + font_id_t *fntid; + language_t *lan; + version_t *ver; + characts_t *chars; + event_t *event; + menu_item_t *menitm; + menuex_item_t *menexitm; + itemex_opt_t *exopt; + raw_data_t *raw; + lvc_t *lvc; + ver_value_t *val; + ver_block_t *blk; + ver_words_t *verw; +} + +%token tIF tIFDEF tIFNDEF tELSE tELIF tENDIF tDEFINED tNL +%token tTYPEDEF tEXTERN +%token <num> NUMBER +%token <str> tSTRING IDENT FILENAME +%token <raw> RAWDATA +%token ACCELERATORS tBITMAP CURSOR DIALOG DIALOGEX MENU MENUEX MESSAGETABLE +%token RCDATA VERSIONINFO STRINGTABLE FONT ICON +%token AUTO3STATE AUTOCHECKBOX AUTORADIOBUTTON CHECKBOX DEFPUSHBUTTON +%token PUSHBUTTON RADIOBUTTON STATE3 /* PUSHBOX */ +%token GROUPBOX COMBOBOX LISTBOX SCROLLBAR +%token CONTROL EDITTEXT +%token RTEXT CTEXT LTEXT +%token BLOCK VALUE +%token SHIFT ALT ASCII VIRTKEY GRAYED CHECKED INACTIVE NOINVERT +%token tPURE IMPURE DISCARDABLE LOADONCALL PRELOAD tFIXED MOVEABLE +%token CLASS CAPTION CHARACTERISTICS EXSTYLE STYLE VERSION LANGUAGE +%token FILEVERSION PRODUCTVERSION FILEFLAGSMASK FILEOS FILETYPE FILEFLAGS FILESUBTYPE +%token MENUBARBREAK MENUBREAK MENUITEM POPUP SEPARATOR +%token HELP +%token tSTRING IDENT RAWDATA +%token tBEGIN tEND +%left LOGOR +%left LOGAND +%left '|' +%left '^' +%left '&' +%left EQ NE +%left '<' LTE '>' GTE +%left '+' '-' +%left '*' '/' +%right '~' '!' NOT + +%type <res> resource_file resource resources resource_definition +%type <stt> stringtable strings +%type <fnt> font +%type <icog> icon +%type <acc> accelerators +%type <event> events +%type <bmp> bitmap +%type <curg> cursor +%type <dlg> dialog dlg_attributes +%type <ctl> ctrls gen_ctrl lab_ctrl ctrl_desc iconinfo +%type <iptr> optional_style helpid +%type <dlgex> dialogex dlgex_attribs +%type <ctl> exctrls gen_exctrl lab_exctrl exctrl_desc +%type <rdt> rcdata +%type <raw> raw_data raw_elements opt_data +%type <veri> versioninfo fix_version +%type <verw> ver_words +%type <blk> ver_blocks ver_block +%type <val> ver_values ver_value +%type <men> menu +%type <menitm> item_definitions menu_body +%type <menex> menuex +%type <menexitm> itemex_definitions menuex_body +%type <exopt> itemex_p_options itemex_options +%type <msg> messagetable +%type <usr> userres +%type <num> item_options +%type <nid> nameid ctlclass usertype +%type <num> acc_opt +%type <iptr> loadmemopts lamo lama +%type <fntid> opt_font opt_exfont +%type <lvc> opt_lvc +%type <lan> opt_language +%type <chars> opt_characts +%type <ver> opt_version +%type <num> expr xpr dummy +%type <iptr> e_expr +%type <iptr> pp_expr pp_constant + +%% + +resource_file + : resources { + resource_t *rsc; + /* First add stringtables to the resource-list */ + rsc = build_stt_resources(sttres); + /* 'build_stt_resources' returns a head and $1 is a tail */ + if($1) + { + $1->next = rsc; + if(rsc) + rsc->prev = $1; + } + else + $1 = rsc; + /* Final statement before were done */ + resource_top = get_resource_head($1); + } + ; + +/* Resources are put into a linked list */ +resources + : /* Empty */ { $$ = NULL; want_rscname = 1; } + | resources resource { + if($2) + { + resource_t *tail = $2; + resource_t *head = $2; + while(tail->next) + tail = tail->next; + while(head->prev) + head = head->prev; + head->prev = $1; + if($1) + $1->next = head; + $$ = tail; + } + else if($1) + { + resource_t *tail = $1; + while(tail->next) + tail = tail->next; + $$ = tail; + } + else + $$ = NULL; + want_rscname = 1; + } + | resources preprocessor { $$ = $1; want_rscname = 1; } + | resources cjunk { $$ = $1; want_rscname = 1; } + ; + +/* The buildin preprocessor */ +preprocessor + : tIF pp_expr tNL { pop_start(); push_if($2 ? *($2) : 0, 0, 0); if($2) free($2);} + | tIFDEF IDENT tNL { pop_start(); push_if(pp_lookup($2->str.cstr) != NULL, 0, 0); } + | tIFNDEF IDENT tNL { pop_start(); push_if(pp_lookup($2->str.cstr) == NULL, 0, 0); } + | tELIF pp_expr tNL { pop_start(); push_if($2 ? *($2) : 0, pop_if(), 0); if($2) free($2); } + | tELSE tNL { pop_start(); push_if(1, pop_if(), 0); } + | tENDIF tNL { pop_if(); } + ; + +pp_expr : pp_constant { $$ = $1; } + | pp_expr LOGOR pp_expr { $$ = new_int($1 && $3 ? (*$1 || *$3) : 0); if($1) free($1); if($3) free($3); } + | pp_expr LOGAND pp_expr { $$ = new_int($1 && $3 ? (*$1 && *$3) : 0); if($1) free($1); if($3) free($3); } + | pp_expr '+' pp_expr { $$ = new_int($1 && $3 ? (*$1 + *$3) : 0); if($1) free($1); if($3) free($3); } + | pp_expr '-' pp_expr { $$ = new_int($1 && $3 ? (*$1 - *$3) : 0); if($1) free($1); if($3) free($3); } + | pp_expr '^' pp_expr { $$ = new_int($1 && $3 ? (*$1 ^ *$3) : 0); if($1) free($1); if($3) free($3); } + | pp_expr EQ pp_expr { $$ = new_int($1 && $3 ? (*$1 == *$3) : 0); if($1) free($1); if($3) free($3); } + | pp_expr NE pp_expr { $$ = new_int($1 && $3 ? (*$1 != *$3) : 0); if($1) free($1); if($3) free($3); } + | pp_expr '<' pp_expr { $$ = new_int($1 && $3 ? (*$1 < *$3) : 0); if($1) free($1); if($3) free($3); } + | pp_expr '>' pp_expr { $$ = new_int($1 && $3 ? (*$1 > *$3) : 0); if($1) free($1); if($3) free($3); } + | pp_expr LTE pp_expr { $$ = new_int($1 && $3 ? (*$1 <= *$3) : 0); if($1) free($1); if($3) free($3); } + | pp_expr GTE pp_expr { $$ = new_int($1 && $3 ? (*$1 >= *$3) : 0); if($1) free($1); if($3) free($3); } + | '~' pp_expr { $$ = $2; if($2) *$2 = ~(*$2); } + | '+' pp_expr { $$ = $2; } + | '-' pp_expr { $$ = $2; if($2) *$2 = -(*$2); } + | '!' pp_expr { $$ = $2; if($2) *$2 = !(*$2); } + | '(' pp_expr ')' { $$ = $2; } + ; + +pp_constant + : NUMBER { $$ = new_int($1); } + | IDENT { $$ = NULL; } + | tDEFINED IDENT { $$ = new_int(pp_lookup($2->str.cstr) != NULL); } + | tDEFINED '(' IDENT ')' { $$ = new_int(pp_lookup($3->str.cstr) != NULL); } + ; + +/* C ignore stuff */ +cjunk : tTYPEDEF { strip_til_semicolon(); } + | tEXTERN { strip_til_semicolon(); } + | IDENT IDENT { strip_til_semicolon(); } + | IDENT '(' { strip_til_parenthesis(); } + | IDENT '*' { strip_til_semicolon(); } + ; + +/* Parse top level resource definitions etc. */ +resource + : nameid resource_definition { + $$ = $2; + if($$) + { + $$->name = $1; + if($1->type == name_ord) + { + chat("Got %s (%d)",get_typename($2),$1->name.i_name); + } + else if($1->type == name_str) + { + chat("Got %s (%s)",get_typename($2),$1->name.s_name->str.cstr); + } + } + } + | stringtable { + /* Don't do anything, stringtables are converted to + * resource_t structures when we are finished parsing and + * the final rule of the parser is reduced (see above) + */ + $$ = NULL; + chat("Got STRINGTABLE"); + } + | opt_language { + if(!win32) + yywarning("LANGUAGE not supported in 16-bit mode"); + if(currentlanguage) + free(currentlanguage); + currentlanguage = $1; + $$ = NULL; + } + ; + +/* FIXME: + * The problem here is that MENU MENU {...} is a valid (!) resource. + * The name-identifier is "MENU". We cannot parse this without generating + * an error. It would mean that a list of keywords must be added here + * and converted into a valid name_id_t structure. + */ +nameid : expr { + $$ = new_name_id(); + $$->type = name_ord; + $$->name.i_name = $1; + want_rscname = 0; + } + | IDENT { + $$ = new_name_id(); + $$->type = name_str; + $$->name.s_name = $1; + want_rscname = 0; + } + ; + +/* get the value for a single resource*/ +resource_definition + : accelerators { $$ = new_resource(res_acc, $1, $1->memopt, $1->lvc.language); } + | bitmap { $$ = new_resource(res_bmp, $1, $1->memopt, currentlanguage); } + | cursor { + resource_t *rsc; + cursor_t *cur; + $$ = rsc = new_resource(res_curg, $1, $1->memopt, currentlanguage); + for(cur = $1->cursorlist; cur; cur = cur->next) + { + rsc->prev = new_resource(res_cur, cur, $1->memopt, currentlanguage); + rsc->prev->next = rsc; + rsc = rsc->prev; + rsc->name = new_name_id(); + rsc->name->type = name_ord; + rsc->name->name.i_name = cur->id; + } + } + | dialog { $$ = new_resource(res_dlg, $1, $1->memopt, $1->lvc.language); } + | dialogex { + if(win32) + $$ = new_resource(res_dlgex, $1, $1->memopt, $1->lvc.language); + else + $$ = NULL; + } + | font { $$=new_resource(res_fnt, $1, $1->memopt, currentlanguage); } + | icon { + resource_t *rsc; + icon_t *ico; + $$ = rsc = new_resource(res_icog, $1, $1->memopt, currentlanguage); + for(ico = $1->iconlist; ico; ico = ico->next) + { + rsc->prev = new_resource(res_ico, ico, $1->memopt, currentlanguage); + rsc->prev->next = rsc; + rsc = rsc->prev; + rsc->name = new_name_id(); + rsc->name->type = name_ord; + rsc->name->name.i_name = ico->id; + } + } + | menu { $$ = new_resource(res_men, $1, $1->memopt, $1->lvc.language); } + | menuex { + if(win32) + $$ = new_resource(res_menex, $1, $1->memopt, $1->lvc.language); + else + $$ = NULL; + } + | messagetable { $$ = new_resource(res_msg, $1, WRC_MO_MOVEABLE | WRC_MO_DISCARDABLE, currentlanguage); } + | rcdata { $$ = new_resource(res_rdt, $1, $1->memopt, $1->lvc.language); } + | userres { $$ = new_resource(res_usr, $1, $1->memopt, currentlanguage); } + | versioninfo { $$ = new_resource(res_ver, $1, WRC_MO_MOVEABLE | WRC_MO_DISCARDABLE, currentlanguage); } + ; + +/* ------------------------------ Bitmap ------------------------------ */ +bitmap : tBITMAP loadmemopts FILENAME { $$ = new_bitmap(load_file($3), $2); } + | tBITMAP loadmemopts raw_data { $$ = new_bitmap($3, $2); } + ; + +/* ------------------------------ Cursor ------------------------------ */ +cursor : CURSOR loadmemopts FILENAME { $$ = new_cursor_group(load_file($3), $2); } + | CURSOR loadmemopts raw_data { $$ = new_cursor_group($3, $2); } + ; + +/* ------------------------------ Font ------------------------------ */ +/* FIXME: Should we allow raw_data here? */ +font : FONT loadmemopts FILENAME { $$ = new_font(load_file($3), $2); } + ; + +/* ------------------------------ Icon ------------------------------ */ +icon : ICON loadmemopts FILENAME { $$ = new_icon_group(load_file($3), $2); } + | ICON loadmemopts raw_data { $$ = new_icon_group($3, $2); } + ; + +/* ------------------------------ MessageTable ------------------------------ */ +/* It might be interesting to implement the MS Message compiler here as well + * to get everything in one source. Might be a future project. + */ +messagetable + : MESSAGETABLE FILENAME { + if(!win32) + yywarning("MESSAGETABLE not supported in 16-bit mode"); + $$ = new_messagetable(load_file($2)); + } + ; + +/* ------------------------------ RCData ------------------------------ */ +rcdata : RCDATA loadmemopts opt_lvc raw_data { + $$ = new_rcdata($4, $2); + if($3) + { + $$->lvc = *($3); + free($3); + } + if(!$$->lvc.language) + $$->lvc.language = dup_language(currentlanguage); + } + ; + +/* ------------------------------ UserType ------------------------------ */ +userres : usertype loadmemopts FILENAME { $$ = new_user($1, load_file($3), $2); } + | usertype loadmemopts raw_data { $$ = new_user($1, $3, $2); } + ; + +/* NOTE: This here is an exception where I do not allow an expression. + * Reason for this is that it is not possible to set the 'yywf' condition + * for flex if loadmemopts is empty. Reading an expression requires a + * lookahead to determine its end. In this case here, that would mean that + * the filename has been read as IDENT or tSTRING, which is incorrect. + * Note also that IDENT cannot be used as a file-name because it is lacking + * the '.'. + */ + +/* I also allow string identifiers as classtypes. Not MS implemented, but + * seems to be reasonable to implement. + */ +/* Allowing anything else than NUMBER makes it very hard to get rid of + * prototypes. So, I remove IDENT to be able to get prototypes out of the + * world. + */ +usertype: NUMBER { + $$ = new_name_id(); + $$->type = name_ord; + $$->name.i_name = $1; + set_yywf(); + } +/* | IDENT { + $$ = new_name_id(); + $$->type = name_str; + $$->name.s_name = $1; + set_yywf(); + } +*/ | tSTRING { + $$ = new_name_id(); + $$->type = name_str; + $$->name.s_name = $1; + set_yywf(); + } + ; + +/* ------------------------------ Accelerator ------------------------------ */ +accelerators + : ACCELERATORS loadmemopts opt_lvc tBEGIN events tEND { + $$ = new_accelerator(); + if($2) + { + $$->memopt = *($2); + free($2); + } + else + { + $$->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE; + } + if(!$5) + yyerror("Accelerator table must have at least one entry"); + $$->events = get_event_head($5); + if($3) + { + $$->lvc = *($3); + free($3); + } + if(!$$->lvc.language) + $$->lvc.language = dup_language(currentlanguage); + } + ; + +events : /* Empty */ { $$=NULL; } + | events tSTRING ',' expr acc_opt { $$=add_string_event($2, $4, $5, $1); } + | events expr ',' expr acc_opt { $$=add_event($2, $4, $5, $1); } + ; + +acc_opt : /* Empty */ { $$=0; } + | acc_opt ',' NOINVERT { $$=$1 | WRC_AF_NOINVERT; } + | acc_opt ',' SHIFT { $$=$1 | WRC_AF_SHIFT; } + | acc_opt ',' CONTROL { $$=$1 | WRC_AF_CONTROL; } + | acc_opt ',' ALT { $$=$1 | WRC_AF_ALT; } + | acc_opt ',' ASCII { $$=$1 | WRC_AF_ASCII; } + | acc_opt ',' VIRTKEY { $$=$1 | WRC_AF_VIRTKEY; } + ; + +/* ------------------------------ Dialog ------------------------------ */ +/* FIXME: Support EXSTYLE in the dialog line itself */ +dialog : DIALOG loadmemopts expr ',' expr ',' expr ',' expr dlg_attributes + tBEGIN ctrls tEND { + if($2) + { + $10->memopt = *($2); + free($2); + } + else + $10->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE | WRC_MO_DISCARDABLE; + $10->x = $3; + $10->y = $5; + $10->width = $7; + $10->height = $9; + $10->controls = get_control_head($12); + $$ = $10; + if(!$$->gotstyle) + { + $$->style = WS_POPUP; + $$->gotstyle = TRUE; + } + if($$->title) + $$->style |= WS_CAPTION; + if($$->font) + $$->style |= DS_SETFONT; + indialog = FALSE; + if(!$$->lvc.language) + $$->lvc.language = dup_language(currentlanguage); + } + ; + +dlg_attributes + : /* Empty */ { $$=new_dialog(); } + | dlg_attributes STYLE expr { $$=dialog_style($3,$1); } + | dlg_attributes EXSTYLE expr { $$=dialog_exstyle($3,$1); } + | dlg_attributes CAPTION tSTRING { $$=dialog_caption($3,$1); } + | dlg_attributes opt_font { $$=dialog_font($2,$1); } + | dlg_attributes CLASS nameid { $$=dialog_class($3,$1); } + | dlg_attributes MENU nameid { $$=dialog_menu($3,$1); } + | dlg_attributes opt_language { $$=dialog_language($2,$1); } + | dlg_attributes opt_characts { $$=dialog_characteristics($2,$1); } + | dlg_attributes opt_version { $$=dialog_version($2,$1); } + ; + +ctrls : /* Empty */ { $$ = NULL; } + | ctrls CONTROL gen_ctrl { $$=ins_ctrl(-1, 0, $3, $1); } + | ctrls EDITTEXT ctrl_desc { $$=ins_ctrl(CT_EDIT, 0, $3, $1); } + | ctrls LISTBOX ctrl_desc { $$=ins_ctrl(CT_LISTBOX, 0, $3, $1); } + | ctrls COMBOBOX ctrl_desc { $$=ins_ctrl(CT_COMBOBOX, 0, $3, $1); } + | ctrls SCROLLBAR ctrl_desc { $$=ins_ctrl(CT_SCROLLBAR, 0, $3, $1); } + | ctrls CHECKBOX lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_CHECKBOX, $3, $1); } + | ctrls DEFPUSHBUTTON lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_DEFPUSHBUTTON, $3, $1); } + | ctrls GROUPBOX lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_GROUPBOX, $3, $1);} + | ctrls PUSHBUTTON lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_PUSHBUTTON, $3, $1); } +/* | ctrls PUSHBOX lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_PUSHBOX, $3, $1); } */ + | ctrls RADIOBUTTON lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_RADIOBUTTON, $3, $1); } + | ctrls AUTO3STATE lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTO3STATE, $3, $1); } + | ctrls STATE3 lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_3STATE, $3, $1); } + | ctrls AUTOCHECKBOX lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTOCHECKBOX, $3, $1); } + | ctrls AUTORADIOBUTTON lab_ctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTORADIOBUTTON, $3, $1); } + | ctrls LTEXT lab_ctrl { $$=ins_ctrl(CT_STATIC, SS_LEFT, $3, $1); } + | ctrls CTEXT lab_ctrl { $$=ins_ctrl(CT_STATIC, SS_CENTER, $3, $1); } + | ctrls RTEXT lab_ctrl { $$=ins_ctrl(CT_STATIC, SS_RIGHT, $3, $1); } + /* special treatment for icons, as the extent is optional */ + | ctrls ICON tSTRING ',' expr ',' expr ',' expr iconinfo { + $10->title = $3; + $10->id = $5; + $10->x = $7; + $10->y = $9; + $$ = ins_ctrl(CT_STATIC, SS_ICON, $10, $1); + } + ; + +lab_ctrl + : tSTRING ',' expr ',' expr ',' expr ',' expr ',' expr optional_style { + $$=new_control(); + $$->title = $1; + $$->id = $3; + $$->x = $5; + $$->y = $7; + $$->width = $9; + $$->height = $11; + if($12) + { + $$->style = *($12); + $$->gotstyle = TRUE; + free($12); + } + } + ; + +ctrl_desc + : expr ',' expr ',' expr ',' expr ',' expr optional_style { + $$ = new_control(); + $$->id = $1; + $$->x = $3; + $$->y = $5; + $$->width = $7; + $$->height = $9; + if($10) + { + $$->style = *($10); + $$->gotstyle = TRUE; + free($10); + } + } + ; + +iconinfo: /* Empty */ + { $$ = new_control(); } + + | ',' expr ',' expr { + $$ = new_control(); + $$->width = $2; + $$->height = $4; + } + | ',' expr ',' expr ',' expr { + $$ = new_control(); + $$->width = $2; + $$->height = $4; + $$->style = $6; + $$->gotstyle = TRUE; + } + | ',' expr ',' expr ',' expr ',' expr { + $$ = new_control(); + $$->width = $2; + $$->height = $4; + $$->style = $6; + $$->gotstyle = TRUE; + $$->exstyle = $8; + $$->gotexstyle = TRUE; + } + ; + +gen_ctrl: tSTRING ',' expr ',' ctlclass ',' expr ',' expr ',' expr ',' expr ',' expr ',' expr { + $$=new_control(); + $$->title = $1; + $$->id = $3; + $$->ctlclass = convert_ctlclass($5); + $$->style = $7; + $$->gotstyle = TRUE; + $$->x = $9; + $$->y = $11; + $$->width = $13; + $$->height = $15; + $$->exstyle = $17; + $$->gotexstyle = TRUE; + } + | tSTRING ',' expr ',' ctlclass ',' expr ',' expr ',' expr ',' expr ',' expr { + $$=new_control(); + $$->title = $1; + $$->id = $3; + $$->ctlclass = convert_ctlclass($5); + $$->style = $7; + $$->gotstyle = TRUE; + $$->x = $9; + $$->y = $11; + $$->width = $13; + $$->height = $15; + } + ; + +opt_font + : FONT expr ',' tSTRING { $$ = new_font_id($2, $4, 0, 0); } + ; + +optional_style /* Abbused once to get optional ExStyle */ + : /* Empty */ { $$ = NULL; } + | ',' expr { $$ = new_int($2); } + ; + +ctlclass + : expr { + $$ = new_name_id(); + $$->type = name_ord; + $$->name.i_name = $1; + } + | tSTRING { + $$ = new_name_id(); + $$->type = name_str; + $$->name.s_name = $1; + } + ; + +/* ------------------------------ DialogEx ------------------------------ */ +dialogex: DIALOGEX loadmemopts expr ',' expr ',' expr ',' expr helpid dlgex_attribs + tBEGIN exctrls tEND { + if(!win32) + yywarning("DIALOGEX not supported in 16-bit mode"); + if($2) + { + $11->memopt = *($2); + free($2); + } + else + $11->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE | WRC_MO_DISCARDABLE; + $11->x = $3; + $11->y = $5; + $11->width = $7; + $11->height = $9; + if($10) + { + $11->helpid = *($10); + $11->gothelpid = TRUE; + free($10); + } + $11->controls = get_control_head($13); + $$ = $11; + if(!$$->gotstyle) + { + $$->style = WS_POPUP; + $$->gotstyle = TRUE; + } + if($$->title) + $$->style |= WS_CAPTION; + if($$->font) + $$->style |= DS_SETFONT; + indialog = FALSE; + if(!$$->lvc.language) + $$->lvc.language = dup_language(currentlanguage); + } + ; + +dlgex_attribs + : /* Empty */ { $$=new_dialogex(); } + | dlgex_attribs STYLE expr { $$=dialogex_style($3,$1); } + | dlgex_attribs EXSTYLE expr { $$=dialogex_exstyle($3,$1); } + | dlgex_attribs CAPTION tSTRING { $$=dialogex_caption($3,$1); } + | dlgex_attribs opt_exfont { $$=dialogex_font($2,$1); } + | dlgex_attribs CLASS nameid { $$=dialogex_class($3,$1); } + | dlgex_attribs MENU nameid { $$=dialogex_menu($3,$1); } + | dlgex_attribs opt_language { $$=dialogex_language($2,$1); } + | dlgex_attribs opt_characts { $$=dialogex_characteristics($2,$1); } + | dlgex_attribs opt_version { $$=dialogex_version($2,$1); } + ; + +exctrls : /* Empty */ { $$ = NULL; } + | exctrls CONTROL gen_exctrl { $$=ins_ctrl(-1, 0, $3, $1); } + | exctrls EDITTEXT exctrl_desc { $$=ins_ctrl(CT_EDIT, 0, $3, $1); } + | exctrls LISTBOX exctrl_desc { $$=ins_ctrl(CT_LISTBOX, 0, $3, $1); } + | exctrls COMBOBOX exctrl_desc { $$=ins_ctrl(CT_COMBOBOX, 0, $3, $1); } + | exctrls SCROLLBAR exctrl_desc { $$=ins_ctrl(CT_SCROLLBAR, 0, $3, $1); } + | exctrls CHECKBOX lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_CHECKBOX, $3, $1); } + | exctrls DEFPUSHBUTTON lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_DEFPUSHBUTTON, $3, $1); } + | exctrls GROUPBOX lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_GROUPBOX, $3, $1);} + | exctrls PUSHBUTTON lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_PUSHBUTTON, $3, $1); } +/* | exctrls PUSHBOX lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_PUSHBOX, $3, $1); } */ + | exctrls RADIOBUTTON lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_RADIOBUTTON, $3, $1); } + | exctrls AUTO3STATE lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTO3STATE, $3, $1); } + | exctrls STATE3 lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_3STATE, $3, $1); } + | exctrls AUTOCHECKBOX lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTOCHECKBOX, $3, $1); } + | exctrls AUTORADIOBUTTON lab_exctrl { $$=ins_ctrl(CT_BUTTON, BS_AUTORADIOBUTTON, $3, $1); } + | exctrls LTEXT lab_exctrl { $$=ins_ctrl(CT_STATIC, SS_LEFT, $3, $1); } + | exctrls CTEXT lab_exctrl { $$=ins_ctrl(CT_STATIC, SS_CENTER, $3, $1); } + | exctrls RTEXT lab_exctrl { $$=ins_ctrl(CT_STATIC, SS_RIGHT, $3, $1); } + /* special treatment for icons, as the extent is optional */ + | exctrls ICON tSTRING ',' expr ',' expr ',' expr iconinfo { + $10->title = $3; + $10->id = $5; + $10->x = $7; + $10->y = $9; + $$ = ins_ctrl(CT_STATIC, SS_ICON, $10, $1); + } + ; + +gen_exctrl + : tSTRING ',' expr ',' ctlclass ',' expr ',' expr ',' expr ',' expr ',' + expr ',' e_expr helpid opt_data { + $$=new_control(); + $$->title = $1; + $$->id = $3; + $$->ctlclass = convert_ctlclass($5); + $$->style = $7; + $$->gotstyle = TRUE; + $$->x = $9; + $$->y = $11; + $$->width = $13; + $$->height = $15; + if($17) + { + $$->exstyle = *($17); + $$->gotexstyle = TRUE; + free($17); + } + if($18) + { + $$->helpid = *($18); + $$->gothelpid = TRUE; + free($18); + } + $$->extra = $19; + } + | tSTRING ',' expr ',' ctlclass ',' expr ',' expr ',' expr ',' expr ',' expr opt_data { + $$=new_control(); + $$->title = $1; + $$->id = $3; + $$->style = $7; + $$->gotstyle = TRUE; + $$->ctlclass = convert_ctlclass($5); + $$->x = $9; + $$->y = $11; + $$->width = $13; + $$->height = $15; + $$->extra = $16; + } + ; + +lab_exctrl + : tSTRING ',' expr ',' expr ',' expr ',' expr ',' expr optional_style opt_data { + $$=new_control(); + $$->title = $1; + $$->id = $3; + $$->x = $5; + $$->y = $7; + $$->width = $9; + $$->height = $11; + if($12) + { + $$->style = *($12); + $$->gotstyle = TRUE; + free($12); + } + $$->extra = $13; + } + ; + +exctrl_desc + : expr ',' expr ',' expr ',' expr ',' expr optional_style opt_data { + $$ = new_control(); + $$->id = $1; + $$->x = $3; + $$->y = $5; + $$->width = $7; + $$->height = $9; + if($10) + { + $$->style = *($10); + $$->gotstyle = TRUE; + free($10); + } + $$->extra = $11; + } + ; + +opt_data: /* Empty */ { $$ = NULL; } + | raw_data { $$ = $1; } + ; + +helpid : /* Empty */ { $$ = NULL; } + | ',' expr { $$ = new_int($2); } + ; + +opt_exfont + : FONT expr ',' tSTRING ',' expr ',' expr { $$ = new_font_id($2, $4, $6, $8); } + ; + +/* ------------------------------ Menu ------------------------------ */ +menu : MENU loadmemopts opt_lvc menu_body { + if(!$4) + yyerror("Menu must contain items"); + $$ = new_menu(); + if($2) + { + $$->memopt = *($2); + free($2); + } + else + $$->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE | WRC_MO_DISCARDABLE; + $$->items = get_item_head($4); + if($3) + { + $$->lvc = *($3); + free($3); + } + if(!$$->lvc.language) + $$->lvc.language = dup_language(currentlanguage); + } + ; + +menu_body + : tBEGIN item_definitions tEND { $$ = $2; } + ; + +item_definitions + : /* Empty */ {$$ = NULL;} + | item_definitions MENUITEM tSTRING ',' expr item_options { + $$=new_menu_item(); + $$->prev = $1; + if($1) + $1->next = $$; + $$->id = $5; + $$->state = $6; + $$->name = $3; + } + | item_definitions MENUITEM SEPARATOR { + $$=new_menu_item(); + $$->prev = $1; + if($1) + $1->next = $$; + } + | item_definitions POPUP tSTRING item_options menu_body { + $$ = new_menu_item(); + $$->prev = $1; + if($1) + $1->next = $$; + $$->popup = get_item_head($5); + $$->name = $3; + } + ; + +/* NOTE: item_options is right recursive because it would introduce + * a shift/reduce conflict on ',' in itemex_options due to the + * empty rule here. The parser is now forced to look beyond the ',' + * before reducing (force shift). + * Right recursion here is not a problem because we cannot expect + * more than 7 parserstack places to be occupied while parsing this + * (who would want to specify a MF_x flag twice?). + */ +item_options + : /* Empty */ { $$ = 0; } + | ',' CHECKED item_options { $$ = $3 | MF_CHECKED; } + | ',' GRAYED item_options { $$ = $3 | MF_GRAYED; } + | ',' HELP item_options { $$ = $3 | MF_HELP; } + | ',' INACTIVE item_options { $$ = $3 | MF_DISABLED; } + | ',' MENUBARBREAK item_options { $$ = $3 | MF_MENUBARBREAK; } + | ',' MENUBREAK item_options { $$ = $3 | MF_MENUBREAK; } + ; + +/* ------------------------------ MenuEx ------------------------------ */ +menuex : MENUEX loadmemopts opt_lvc menuex_body { + if(!win32) + yywarning("MENUEX not supported in 16-bit mode"); + if(!$4) + yyerror("MenuEx must contain items"); + $$ = new_menuex(); + if($2) + { + $$->memopt = *($2); + free($2); + } + else + $$->memopt = WRC_MO_MOVEABLE | WRC_MO_PURE | WRC_MO_DISCARDABLE; + $$->items = get_itemex_head($4); + if($3) + { + $$->lvc = *($3); + free($3); + } + if(!$$->lvc.language) + $$->lvc.language = dup_language(currentlanguage); + } + ; + +menuex_body + : tBEGIN itemex_definitions tEND { $$ = $2; } + ; + +itemex_definitions + : /* Empty */ {$$ = NULL; } + | itemex_definitions MENUITEM tSTRING itemex_options { + $$ = new_menuex_item(); + $$->prev = $1; + if($1) + $1->next = $$; + $$->name = $3; + $$->id = $4->id; + $$->type = $4->type; + $$->state = $4->state; + $$->helpid = $4->helpid; + $$->gotid = $4->gotid; + $$->gottype = $4->gottype; + $$->gotstate = $4->gotstate; + $$->gothelpid = $4->gothelpid; + free($4); + } + | itemex_definitions MENUITEM SEPARATOR { + $$ = new_menuex_item(); + $$->prev = $1; + if($1) + $1->next = $$; + } + | itemex_definitions POPUP tSTRING itemex_p_options menuex_body { + $$ = new_menuex_item(); + $$->prev = $1; + if($1) + $1->next = $$; + $$->popup = get_itemex_head($5); + $$->name = $3; + $$->id = $4->id; + $$->type = $4->type; + $$->state = $4->state; + $$->helpid = $4->helpid; + $$->gotid = $4->gotid; + $$->gottype = $4->gottype; + $$->gotstate = $4->gotstate; + $$->gothelpid = $4->gothelpid; + free($4); + } + ; + +itemex_options + : /* Empty */ { $$ = new_itemex_opt(0, 0, 0, 0); } + | ',' expr { + $$ = new_itemex_opt($2, 0, 0, 0); + $$->gotid = TRUE; + } + | ',' e_expr ',' e_expr item_options { + $$ = new_itemex_opt($2 ? *($2) : 0, $4 ? *($4) : 0, $5, 0); + $$->gotid = TRUE; + $$->gottype = TRUE; + $$->gotstate = TRUE; + if($2) free($2); + if($4) free($4); + } + | ',' e_expr ',' e_expr ',' expr { + $$ = new_itemex_opt($2 ? *($2) : 0, $4 ? *($4) : 0, $6, 0); + $$->gotid = TRUE; + $$->gottype = TRUE; + $$->gotstate = TRUE; + if($2) free($2); + if($4) free($4); + } + ; + +itemex_p_options + : /* Empty */ { $$ = new_itemex_opt(0, 0, 0, 0); } + | ',' expr { + $$ = new_itemex_opt($2, 0, 0, 0); + $$->gotid = TRUE; + } + | ',' e_expr ',' expr { + $$ = new_itemex_opt($2 ? *($2) : 0, $4, 0, 0); + if($2) free($2); + $$->gotid = TRUE; + $$->gottype = TRUE; + } + | ',' e_expr ',' e_expr ',' expr { + $$ = new_itemex_opt($2 ? *($2) : 0, $4 ? *($4) : 0, $6, 0); + if($2) free($2); + if($4) free($4); + $$->gotid = TRUE; + $$->gottype = TRUE; + $$->gotstate = TRUE; + } + | ',' e_expr ',' e_expr ',' e_expr ',' expr { + $$ = new_itemex_opt($2 ? *($2) : 0, $4 ? *($4) : 0, $6 ? *($6) : 0, $8); + if($2) free($2); + if($4) free($4); + if($6) free($6); + $$->gotid = TRUE; + $$->gottype = TRUE; + $$->gotstate = TRUE; + $$->gothelpid = TRUE; + } + ; + +/* ------------------------------ StringTable ------------------------------ */ +/* Stringtables are parsed differently than other resources because their + * layout is substantially different from other resources. + * The table is parsed through a _global_ variable 'tagstt' which holds the + * current stringtable descriptor (stringtable_t *) and 'sttres' that holds a + * list of stringtables of different languages. + */ +stringtable + : stt_head tBEGIN strings tEND { + if(!$3) + { + yyerror("Stringtable must have at least one entry"); + } + else + { + stringtable_t *stt; + /* Check if we added to a language table or created + * a new one. + */ + for(stt = sttres; stt; stt = stt->next) + { + if(stt == tagstt) + break; + } + if(!stt) + { + /* It is a new one */ + if(sttres) + { + sttres->prev = tagstt; + tagstt->next = sttres; + sttres = tagstt; + } + else + sttres = tagstt; + } + /* Else were done */ + } + if(tagstt_memopt) + { + free(tagstt_memopt); + tagstt_memopt = NULL; + } + + $$ = tagstt; + } + ; + +/* This is to get the language of the currently parsed stringtable */ +stt_head: STRINGTABLE loadmemopts opt_lvc { + if((tagstt = find_stringtable($3)) == NULL) + tagstt = new_stringtable($3); + tagstt_memopt = $2; + tagstt_version = $3->version; + tagstt_characts = $3->characts; + if($3) + free($3); + } + ; + +strings : /* Empty */ { $$ = NULL; } + | strings expr opt_comma tSTRING { + int i; + assert(tagstt != NULL); + /* Search for the ID */ + for(i = 0; i < tagstt->nentries; i++) + { + if(tagstt->entries[i].id == $2) + yyerror("Stringtable ID %d already in use", $2); + } + /* If we get here, then we have a new unique entry */ + tagstt->nentries++; + tagstt->entries = xrealloc(tagstt->entries, sizeof(tagstt->entries[0]) * tagstt->nentries); + tagstt->entries[tagstt->nentries-1].id = $2; + tagstt->entries[tagstt->nentries-1].str = $4; + if(tagstt_memopt) + tagstt->entries[tagstt->nentries-1].memopt = *tagstt_memopt; + else + tagstt->entries[tagstt->nentries-1].memopt = WRC_MO_MOVEABLE | WRC_MO_DISCARDABLE | WRC_MO_PURE; + tagstt->entries[tagstt->nentries-1].version = tagstt_version; + tagstt->entries[tagstt->nentries-1].characts = tagstt_characts; + + if(!win32 && $4->size > 254) + yyerror("Stringtable entry more than 254 characters"); + if(win32 && $4->size > 65534) /* Hmm..., does this happen? */ + yyerror("Stringtable entry more than 65534 characters (probably something else that went wrong)"); + $$ = tagstt; + } + ; + +opt_comma /* There seem to be two ways to specify a stringtable... */ + : /* Empty */ + | ',' + ; + +/* ------------------------------ VersionInfo ------------------------------ */ +versioninfo + : VERSIONINFO fix_version tBEGIN ver_blocks tEND { + $$ = $2; + $2->blocks = get_ver_block_head($4); + } + ; + +fix_version + : /* Empty */ { $$ = new_versioninfo(); } + | fix_version FILEVERSION expr ',' expr ',' expr ',' expr { + if($1->gotit.fv) + yyerror("FILEVERSION already defined"); + $$ = $1; + $$->filever_maj1 = $3; + $$->filever_maj2 = $5; + $$->filever_min1 = $7; + $$->filever_min2 = $9; + $$->gotit.fv = 1; + } + | fix_version PRODUCTVERSION expr ',' expr ',' expr ',' expr { + if($1->gotit.pv) + yyerror("PRODUCTVERSION already defined"); + $$ = $1; + $$->prodver_maj1 = $3; + $$->prodver_maj2 = $5; + $$->prodver_min1 = $7; + $$->prodver_min2 = $9; + $$->gotit.pv = 1; + } + | fix_version FILEFLAGS expr { + if($1->gotit.ff) + yyerror("FILEFLAGS already defined"); + $$ = $1; + $$->fileflags = $3; + $$->gotit.ff = 1; + } + | fix_version FILEFLAGSMASK expr { + if($1->gotit.ffm) + yyerror("FILEFLAGSMASK already defined"); + $$ = $1; + $$->fileflagsmask = $3; + $$->gotit.ffm = 1; + } + | fix_version FILEOS expr { + if($1->gotit.fo) + yyerror("FILEOS already defined"); + $$ = $1; + $$->fileos = $3; + $$->gotit.fo = 1; + } + | fix_version FILETYPE expr { + if($1->gotit.ft) + yyerror("FILETYPE already defined"); + $$ = $1; + $$->filetype = $3; + $$->gotit.ft = 1; + } + | fix_version FILESUBTYPE expr { + if($1->gotit.fst) + yyerror("FILESUBTYPE already defined"); + $$ = $1; + $$->filesubtype = $3; + $$->gotit.fst = 1; + } + ; + +ver_blocks + : /* Empty */ { $$ = NULL; } + | ver_blocks ver_block { + $$ = $2; + $$->prev = $1; + if($1) + $1->next = $$; + } + ; + +ver_block + : BLOCK tSTRING tBEGIN ver_values tEND { + $$ = new_ver_block(); + $$->name = $2; + $$->values = get_ver_value_head($4); + } + ; + +ver_values + : /* Empty */ { $$ = NULL; } + | ver_values ver_value { + $$ = $2; + $$->prev = $1; + if($1) + $1->next = $$; + } + ; + +ver_value + : ver_block { + $$ = new_ver_value(); + $$->type = val_block; + $$->value.block = $1; + } + | VALUE tSTRING ',' tSTRING { + $$ = new_ver_value(); + $$->type = val_str; + $$->key = $2; + $$->value.str = $4; + } + | VALUE tSTRING ',' ver_words { + $$ = new_ver_value(); + $$->type = val_words; + $$->key = $2; + $$->value.words = $4; + } + ; + +ver_words + : expr { $$ = new_ver_words($1); } + | ver_words ',' expr { $$ = add_ver_words($1, $3); } + ; + +/* ------------------------------ Memory options ------------------------------ */ +loadmemopts + : /* Empty */ { $$ = NULL; } + | loadmemopts lamo { + if($1) + { + *($1) |= *($2); + $$ = $1; + free($2); + } + else + $$ = $2; + } + | loadmemopts lama { + if($1) + { + *($1) &= *($2); + $$ = $1; + free($2); + } + else + { + *$2 &= WRC_MO_MOVEABLE | WRC_MO_DISCARDABLE | WRC_MO_PURE; + $$ = $2; + } + } + ; + +lamo : PRELOAD { $$ = new_int(WRC_MO_PRELOAD); } + | MOVEABLE { $$ = new_int(WRC_MO_MOVEABLE); } + | DISCARDABLE { $$ = new_int(WRC_MO_DISCARDABLE); } + | tPURE { $$ = new_int(WRC_MO_PURE); } + ; + +lama : LOADONCALL { $$ = new_int(~WRC_MO_PRELOAD); } + | tFIXED { $$ = new_int(~WRC_MO_MOVEABLE); } + | IMPURE { $$ = new_int(~WRC_MO_PURE); } + ; + +/* ------------------------------ Win32 options ------------------------------ */ +opt_lvc : /* Empty */ { $$ = new_lvc(); } + | opt_lvc opt_language { + if(!win32) + yywarning("LANGUAGE not supported in 16-bit mode"); + if($1->language) + yyerror("Language already defined"); + $$ = $1; + $1->language = $2; + } + | opt_lvc opt_characts { + if(!win32) + yywarning("CHARACTERISTICS not supported in 16-bit mode"); + if($1->characts) + yyerror("Characteristics already defined"); + $$ = $1; + $1->characts = $2; + } + | opt_lvc opt_version { + if(!win32) + yywarning("VERSION not supported in 16-bit mode"); + if($1->version) + yyerror("Version already defined"); + $$ = $1; + $1->version = $2; + } + ; + +opt_language + : LANGUAGE expr ',' expr { $$ = new_language($2, $4); } + ; + +opt_characts + : CHARACTERISTICS expr { $$ = new_characts($2); } + ; + +opt_version + : VERSION expr { $$ = new_version($2); } + ; + +/* ------------------------------ Raw data handking ------------------------------ */ +raw_data: tBEGIN raw_elements tEND { $$ = $2; } + ; + +raw_elements + : RAWDATA { $$ = $1; } + | NUMBER { $$ = int2raw_data($1); } + | tSTRING { $$ = str2raw_data($1); } + | raw_elements opt_comma RAWDATA { $$ = merge_raw_data($1, $3); free($3->data); free($3); } + | raw_elements opt_comma NUMBER { $$ = merge_raw_data_int($1, $3); } + | raw_elements opt_comma tSTRING { $$ = merge_raw_data_str($1, $3); } + ; + +/* ------------------------------ Win32 expressions ------------------------------ */ +/* All win16 numbers are also handled here. This is inconsistent with MS' + * resource compiler, but what the heck, its just handy to have. + */ +e_expr : /* Empty */ { $$ = 0; } + | expr { $$ = new_int($1); } + ; +expr : dummy xpr { $$ = ($2) & andmask; } + ; + +dummy : /* Empty */ { $$ = 0; andmask = -1; } + ; + +xpr : xpr '+' xpr { $$ = ($1) + ($3); } + | xpr '-' xpr { $$ = ($1) - ($3); } + | xpr '|' xpr { $$ = ($1) | ($3); } + | xpr '&' xpr { $$ = ($1) & ($3); } + | xpr '*' xpr { $$ = ($1) * ($3); } + | xpr '/' xpr { $$ = ($1) / ($3); } + | '~' xpr { $$ = ~($2); } + | '-' xpr { $$ = -($2); } /* FIXME: shift/reduce conflict */ +/* | '+' xpr { $$ = $2; } */ + | '(' xpr ')' { $$ = $2; } + | NUMBER { $$ = $1; want_rscname = 0; } + | NOT NUMBER { $$ = 0; andmask &= ~($2); } + ; +%% +/* Dialog specific functions */ +dialog_t *dialog_style(int st, dialog_t *dlg) +{ + DWORD s = 0; + assert(dlg != NULL); + if(dlg->gotstyle) + { + yywarning("Style already defined, or-ing together"); + s = dlg->style; + } + dlg->style = st | s; + dlg->gotstyle = TRUE; + return dlg; +} + +dialog_t *dialog_exstyle(int st, dialog_t *dlg) +{ + DWORD s = 0; + assert(dlg != NULL); + if(dlg->gotexstyle) + { + yywarning("ExStyle already defined, or-ing together"); + s = dlg->style; + } + dlg->exstyle = st | s; + dlg->gotexstyle = TRUE; + return dlg; +} + +dialog_t *dialog_caption(string_t *s, dialog_t *dlg) +{ + assert(dlg != NULL); + if(dlg->title) + yyerror("Caption already defined"); + dlg->title = s; + return dlg; +} + +dialog_t *dialog_font(font_id_t *f, dialog_t *dlg) +{ + assert(dlg != NULL); + if(dlg->font) + yyerror("Font already defined"); + dlg->font = f; + return dlg; +} + +dialog_t *dialog_class(name_id_t *n, dialog_t *dlg) +{ + assert(dlg != NULL); + if(dlg->dlgclass) + yyerror("Class already defined"); + dlg->dlgclass = n; + return dlg; +} + +dialog_t *dialog_menu(name_id_t *m, dialog_t *dlg) +{ + assert(dlg != NULL); + if(dlg->menu) + yyerror("Menu already defined"); + dlg->menu = m; + return dlg; +} + +dialog_t *dialog_language(language_t *l, dialog_t *dlg) +{ + assert(dlg != NULL); + if(dlg->lvc.language) + yyerror("Language already defined"); + dlg->lvc.language = l; + return dlg; +} + +dialog_t *dialog_characteristics(characts_t *c, dialog_t *dlg) +{ + assert(dlg != NULL); + if(dlg->lvc.characts) + yyerror("Characteristics already defined"); + dlg->lvc.characts = c; + return dlg; +} + +dialog_t *dialog_version(version_t *v, dialog_t *dlg) +{ + assert(dlg != NULL); + if(dlg->lvc.version) + yyerror("Version already defined"); + dlg->lvc.version = v; + return dlg; +} + +/* Controls specific functions */ +control_t *ins_ctrl(int type, int style, control_t *ctrl, control_t *prev) +{ + assert(ctrl != NULL); + ctrl->prev = prev; + if(prev) + prev->next = ctrl; + if(type != -1) + { + ctrl->ctlclass = new_name_id(); + ctrl->ctlclass->type = name_ord; + ctrl->ctlclass->name.i_name = type; + } + + /* Hm... this seems to be jammed in at all time... */ + ctrl->style |= WS_CHILD | WS_VISIBLE; + switch(type) + { + case CT_BUTTON: + ctrl->style |= style; + if(style != BS_GROUPBOX && style != BS_RADIOBUTTON) + ctrl->style |= WS_TABSTOP; + break; + case CT_EDIT: + ctrl->style |= WS_TABSTOP | WS_BORDER; + break; + case CT_LISTBOX: + ctrl->style |= LBS_NOTIFY | WS_BORDER; + break; + case CT_COMBOBOX: + ctrl->style |= CBS_SIMPLE; + break; + case CT_STATIC: + ctrl->style |= style; + if(style == SS_CENTER || style == SS_LEFT || style == SS_RIGHT) + ctrl->style |= WS_GROUP; + break; + } + + if(!ctrl->gotstyle) /* Handle default style setting */ + { + switch(type) + { + case CT_EDIT: + ctrl->style |= ES_LEFT; + break; + case CT_LISTBOX: + ctrl->style |= LBS_NOTIFY; + break; + case CT_COMBOBOX: + ctrl->style |= CBS_SIMPLE | WS_TABSTOP; + break; + case CT_SCROLLBAR: + ctrl->style |= SBS_HORZ; + break; + case CT_BUTTON: + switch(style) + { + case BS_CHECKBOX: + case BS_DEFPUSHBUTTON: + case BS_PUSHBUTTON: + case BS_GROUPBOX: +/* case BS_PUSHBOX: */ + case BS_AUTORADIOBUTTON: + case BS_AUTO3STATE: + case BS_3STATE: + case BS_AUTOCHECKBOX: + ctrl->style |= WS_TABSTOP; + break; + default: + yywarning("Unknown default button control-style 0x%08x", style); + case BS_RADIOBUTTON: + break; + } + break; + + case CT_STATIC: + switch(style) + { + case SS_LEFT: + case SS_RIGHT: + case SS_CENTER: + ctrl->style |= WS_GROUP; + break; + case SS_ICON: /* Special case */ + break; + default: + yywarning("Unknown default static control-style 0x%08x", style); + break; + } + break; + case -1: /* Generic control */ + goto byebye; + + default: + yyerror("Internal error (report this): Got weird control type 0x%08x", type); + } + } + + /* The SS_ICON flag is always forced in for icon controls */ + if(type == CT_STATIC && style == SS_ICON) + ctrl->style |= SS_ICON; + + ctrl->gotstyle = TRUE; +byebye: + return ctrl; +} + +name_id_t *convert_ctlclass(name_id_t *cls) +{ + char *cc; + int iclass; + + if(cls->type == name_ord) + return cls; + assert(cls->type == name_str); + if(cls->type == str_unicode) + { + yyerror("Don't yet support unicode class comparison"); + } + else + cc = cls->name.s_name->str.cstr; + + if(!stricmp("BUTTON", cc)) + iclass = CT_BUTTON; + else if(!stricmp("COMBOBOX", cc)) + iclass = CT_COMBOBOX; + else if(!stricmp("LISTBOX", cc)) + iclass = CT_LISTBOX; + else if(!stricmp("EDIT", cc)) + iclass = CT_EDIT; + else if(!stricmp("STATIC", cc)) + iclass = CT_STATIC; + else if(!stricmp("SCROLLBAR", cc)) + iclass = CT_SCROLLBAR; + else + return cls; /* No default, return user controlclass */ + + free(cls->name.s_name->str.cstr); + free(cls->name.s_name); + cls->type = name_ord; + cls->name.i_name = iclass; + return cls; +} + +/* DialogEx specific functions */ +dialogex_t *dialogex_style(int st, dialogex_t *dlg) +{ + DWORD s = 0; + assert(dlg != NULL); + if(dlg->gotstyle) + { + yywarning("Style already defined, or-ing together"); + s = dlg->style; + } + dlg->style = st | s; + dlg->gotstyle = TRUE; + return dlg; +} + +dialogex_t *dialogex_exstyle(int st, dialogex_t *dlg) +{ + DWORD s = 0; + assert(dlg != NULL); + if(dlg->gotexstyle) + { + yywarning("ExStyle already defined, or-ing together"); + s = dlg->exstyle; + } + dlg->exstyle = st | s; + dlg->gotexstyle = TRUE; + return dlg; +} + +dialogex_t *dialogex_caption(string_t *s, dialogex_t *dlg) +{ + assert(dlg != NULL); + if(dlg->title) + yyerror("Caption already defined"); + dlg->title = s; + return dlg; +} + +dialogex_t *dialogex_font(font_id_t *f, dialogex_t *dlg) +{ + assert(dlg != NULL); + if(dlg->font) + yyerror("Font already defined"); + dlg->font = f; + return dlg; +} + +dialogex_t *dialogex_class(name_id_t *n, dialogex_t *dlg) +{ + assert(dlg != NULL); + if(dlg->dlgclass) + yyerror("Class already defined"); + dlg->dlgclass = n; + return dlg; +} + +dialogex_t *dialogex_menu(name_id_t *m, dialogex_t *dlg) +{ + assert(dlg != NULL); + if(dlg->menu) + yyerror("Menu already defined"); + dlg->menu = m; + return dlg; +} + +dialogex_t *dialogex_language(language_t *l, dialogex_t *dlg) +{ + assert(dlg != NULL); + if(dlg->lvc.language) + yyerror("Language already defined"); + dlg->lvc.language = l; + return dlg; +} + +dialogex_t *dialogex_characteristics(characts_t *c, dialogex_t *dlg) +{ + assert(dlg != NULL); + if(dlg->lvc.characts) + yyerror("Characteristics already defined"); + dlg->lvc.characts = c; + return dlg; +} + +dialogex_t *dialogex_version(version_t *v, dialogex_t *dlg) +{ + assert(dlg != NULL); + if(dlg->lvc.version) + yyerror("Version already defined"); + dlg->lvc.version = v; + return dlg; +} + +/* Accelerator specific functions */ +event_t *add_event(int key, int id, int flags, event_t *prev) +{ + event_t *ev = new_event(); + + if((flags & (WRC_AF_VIRTKEY | WRC_AF_ASCII)) == (WRC_AF_VIRTKEY | WRC_AF_ASCII)) + yyerror("Cannot use both ASCII and VIRTKEY"); + + ev->key = key; + ev->id = id; + ev->flags = flags & ~WRC_AF_ASCII; + ev->prev = prev; + if(prev) + prev->next = ev; + return ev; +} + +event_t *add_string_event(string_t *key, int id, int flags, event_t *prev) +{ + int keycode; + event_t *ev = new_event(); + + if(key->type != str_char) + yyerror("Key code must be an ascii string"); + + if((flags & WRC_AF_VIRTKEY) && (!isupper(key->str.cstr[0]) || !isdigit(key->str.cstr[0]))) + yyerror("VIRTKEY code is not equal to ascii value"); + + if(key->str.cstr[0] == '^') + { + keycode = toupper(key->str.cstr[1]) - '@'; + if(keycode >= ' ') + yyerror("Control-code out of range"); + } + else + keycode = key->str.cstr[0]; + ev->key = keycode; + ev->id = id; + ev->flags = flags & ~WRC_AF_ASCII; + ev->prev = prev; + if(prev) + prev->next = ev; + return ev; +} + +/* MenuEx specific functions */ +itemex_opt_t *new_itemex_opt(int id, int type, int state, int helpid) +{ + itemex_opt_t *opt = (itemex_opt_t *)xmalloc(sizeof(itemex_opt_t)); + opt->id = id; + opt->type = type; + opt->state = state; + opt->helpid = helpid; + return opt; +} + +/* Raw data functions */ +raw_data_t *load_file(string_t *name) +{ + FILE *fp; + raw_data_t *rd; + if(name->type != str_char) + yyerror("Filename must be ASCII string"); + + fp = fopen(name->str.cstr, "rb"); + if(!fp) + yyerror("Cannot open file %s", name); + rd = new_raw_data(); + fseek(fp, 0, SEEK_END); + rd->size = ftell(fp); + fseek(fp, 0, SEEK_SET); + rd->data = (char *)xmalloc(rd->size); + fread(rd->data, rd->size, 1, fp); + fclose(fp); + HEAPCHECK(); + return rd; +} + +raw_data_t *int2raw_data(int i) +{ + raw_data_t *rd; + rd = new_raw_data(); + rd->size = sizeof(short); + rd->data = (char *)xmalloc(rd->size); + *(short *)(rd->data) = (short)i; + return rd; +} + +raw_data_t *str2raw_data(string_t *str) +{ + raw_data_t *rd; + rd = new_raw_data(); + rd->size = str->size * (str->type == str_char ? 1 : 2); + rd->data = (char *)xmalloc(rd->size); + memcpy(rd->data, str->str.cstr, rd->size); + return rd; +} + +raw_data_t *merge_raw_data(raw_data_t *r1, raw_data_t *r2) +{ + r1->data = xrealloc(r1->data, r1->size + r2->size); + memcpy(r1->data + r1->size, r2->data, r2->size); + r1->size += r2->size; + return r1; +} + +raw_data_t *merge_raw_data_int(raw_data_t *r1, int i) +{ + raw_data_t *t = int2raw_data(i); + merge_raw_data(r1, t); + free(t->data); + free(t); + return r1; +} + +raw_data_t *merge_raw_data_str(raw_data_t *r1, string_t *str) +{ + raw_data_t *t = str2raw_data(str); + merge_raw_data(r1, t); + free(t->data); + free(t); + return r1; +} + +/* Function the go back in a list to get the head */ +menu_item_t *get_item_head(menu_item_t *p) +{ + if(!p) + return NULL; + while(p->prev) + p = p->prev; + return p; +} + +menuex_item_t *get_itemex_head(menuex_item_t *p) +{ + if(!p) + return NULL; + while(p->prev) + p = p->prev; + return p; +} + +resource_t *get_resource_head(resource_t *p) +{ + if(!p) + return NULL; + while(p->prev) + p = p->prev; + return p; +} + +ver_block_t *get_ver_block_head(ver_block_t *p) +{ + if(!p) + return NULL; + while(p->prev) + p = p->prev; + return p; +} + +ver_value_t *get_ver_value_head(ver_value_t *p) +{ + if(!p) + return NULL; + while(p->prev) + p = p->prev; + return p; +} + +control_t *get_control_head(control_t *p) +{ + if(!p) + return NULL; + while(p->prev) + p = p->prev; + return p; +} + +event_t *get_event_head(event_t *p) +{ + if(!p) + return NULL; + while(p->prev) + p = p->prev; + return p; +} + +/* Find a stringtable with given language */ +stringtable_t *find_stringtable(lvc_t *lvc) +{ + stringtable_t *stt; + + assert(lvc != NULL); + + if(!lvc->language) + lvc->language = currentlanguage; + + for(stt = sttres; stt; stt = stt->next) + { + if(stt->lvc.language->id == lvc->language->id + && stt->lvc.language->id == lvc->language->id) + { + /* Found a table with the same language */ + /* The version and characteristics are now handled + * in the generation of the individual stringtables. + * This enables localized analysis. + if((stt->lvc.version && lvc->version && *(stt->lvc.version) != *(lvc->version)) + || (!stt->lvc.version && lvc->version) + || (stt->lvc.version && !lvc->version)) + yywarning("Stringtable's versions are not the same, using first definition"); + + if((stt->lvc.characts && lvc->characts && *(stt->lvc.characts) != *(lvc->characts)) + || (!stt->lvc.characts && lvc->characts) + || (stt->lvc.characts && !lvc->characts)) + yywarning("Stringtable's characteristics are not the same, using first definition"); + */ + return stt; + } + } + return NULL; +} + +/* qsort sorting function for string table entries */ +#define STE(p) ((stt_entry_t *)(p)) +int sort_stt_entry(const void *e1, const void *e2) +{ + return STE(e1)->id - STE(e2)->id; +} +#undef STE + +resource_t *build_stt_resources(stringtable_t *stthead) +{ + stringtable_t *stt; + stringtable_t *newstt; + resource_t *rsc; + resource_t *rsclist = NULL; + resource_t *rsctail = NULL; + int i; + int j; + DWORD andsum; + DWORD orsum; + characts_t *characts; + version_t *version; + + if(!stthead) + return NULL; + + /* For all languages defined */ + for(stt = stthead; stt; stt = stt->next) + { + assert(stt->nentries > 0); + + /* Sort the entries */ + if(stt->nentries > 1) + qsort(stt->entries, stt->nentries, sizeof(stt->entries[0]), sort_stt_entry); + + for(i = 0; i < stt->nentries; ) + { + newstt = new_stringtable(&stt->lvc); + newstt->entries = (stt_entry_t *)xmalloc(16 * sizeof(stt_entry_t)); + newstt->nentries = 16; + newstt->idbase = stt->entries[i].id & ~0xf; + for(j = 0; j < 16 && i < stt->nentries; j++) + { + if(stt->entries[i].id - newstt->idbase == j) + { + newstt->entries[j] = stt->entries[i]; + i++; + } + } + andsum = ~0; + orsum = 0; + characts = NULL; + version = NULL; + /* Check individual memory options and get + * the first characteristics/version + */ + for(j = 0; j < 16; j++) + { + if(!newstt->entries[j].str) + continue; + andsum &= newstt->entries[j].memopt; + orsum |= newstt->entries[j].memopt; + if(!characts) + characts = newstt->entries[j].characts; + if(!version) + version = newstt->entries[j].version; + } + if(andsum != orsum) + { + warning("Stringtable's memory options are not equal (idbase: %d)", newstt->idbase); + } + /* Check version and characteristics */ + for(j = 0; j < 16; j++) + { + if(characts + && newstt->entries[j].characts + && *newstt->entries[j].characts != *characts) + warning("Stringtable's characteristics are not the same (idbase: %d)", newstt->idbase); + if(version + && newstt->entries[j].version + && *newstt->entries[j].version != *version) + warning("Stringtable's versions are not the same (idbase: %d)", newstt->idbase); + } + rsc = new_resource(res_stt, newstt, newstt->memopt, newstt->lvc.language); + rsc->name = new_name_id(); + rsc->name->type = name_ord; + rsc->name->name.i_name = (newstt->idbase >> 4) + 1; + rsc->memopt = andsum; /* Set to least common denominator */ + newstt->memopt = andsum; + newstt->lvc.characts = characts; + newstt->lvc.version = version; + if(!rsclist) + { + rsclist = rsc; + rsctail = rsc; + } + else + { + rsctail->next = rsc; + rsc->prev = rsctail; + rsctail = rsc; + } + } + } + return rsclist; +} + +/* Cursor and icon splitter functions */ +int alloc_icon_id(void) +{ + static int icon_id = 1; + return icon_id++; +} + +int alloc_cursor_id(void) +{ + static int cursor_id = 1; + return cursor_id++; +} + +#define BPTR(base) ((char *)(rd->data + (base))) +#define WPTR(base) ((WORD *)(rd->data + (base))) +#define DPTR(base) ((DWORD *)(rd->data + (base))) +void split_icons(raw_data_t *rd, icon_group_t *icog, int *nico) +{ + int cnt; + int i; + icon_dir_entry_t *ide; + icon_t *ico; + icon_t *list = NULL; + + /* FIXME: Distinguish between normal and animated icons (RIFF format) */ + if(WPTR(0)[1] != 1) + yyerror("Icon resource data has invalid type id %d", WPTR(0)[1]); + cnt = WPTR(0)[2]; + ide = (icon_dir_entry_t *)&(WPTR(0)[3]); + for(i = 0; i < cnt; i++) + { + ico = new_icon(); + ico->id = alloc_icon_id(); + if(ide[i].offset > rd->size + || ide[i].offset + ide[i].ressize > rd->size) + yyerror("Icon resource data corrupt"); + ico->width = ide[i].width; + ico->height = ide[i].height; + ico->nclr = ide[i].nclr; + ico->planes = ide[i].planes; + ico->bits = ide[i].bits; + if(!ico->planes) + { + /* Argh! They did not fill out the resdir structure */ + ico->planes = ((BITMAPINFOHEADER *)BPTR(ide[i].offset))->biPlanes; + } + if(!ico->bits) + { + /* Argh! They did not fill out the resdir structure */ + ico->bits = ((BITMAPINFOHEADER *)BPTR(ide[i].offset))->biBitCount; + } + ico->data = new_raw_data(); + copy_raw_data(ico->data, rd, ide[i].offset, ide[i].ressize); + if(!list) + { + list = ico; + } + else + { + ico->next = list; + list->prev = ico; + list = ico; + } + } + icog->iconlist = list; + *nico = cnt; +} + +void split_cursors(raw_data_t *rd, cursor_group_t *curg, int *ncur) +{ + int cnt; + int i; + cursor_dir_entry_t *cde; + cursor_t *cur; + cursor_t *list = NULL; + + /* FIXME: Distinguish between normal and animated cursors (RIFF format)*/ + if(WPTR(0)[1] != 2) + yyerror("Cursor resource data has invalid type id %d", WPTR(0)[1]); + cnt = WPTR(0)[2]; + cde = (cursor_dir_entry_t *)&(WPTR(0)[3]); + for(i = 0; i < cnt; i++) + { + cur = new_cursor(); + cur->id = alloc_cursor_id(); + if(cde[i].offset > rd->size + || cde[i].offset + cde[i].ressize > rd->size) + yyerror("Cursor resource data corrupt"); + cur->width = cde[i].width; + cur->height = cde[i].height; + cur->nclr = cde[i].nclr; + /* The next two are to support color cursors */ + cur->planes = ((BITMAPINFOHEADER *)BPTR(cde[i].offset))->biPlanes; + cur->bits = ((BITMAPINFOHEADER *)BPTR(cde[i].offset))->biBitCount; + if(!win32 && (cur->planes != 1 || cur->bits != 1)) + yywarning("Win16 cursor contains colors"); + cur->xhot = cde[i].xhot; + cur->yhot = cde[i].yhot; + cur->data = new_raw_data(); + copy_raw_data(cur->data, rd, cde[i].offset, cde[i].ressize); + if(!list) + { + list = cur; + } + else + { + cur->next = list; + list->prev = cur; + list = cur; + } + } + curg->cursorlist = list; + *ncur = cnt; +} + +#undef BPTR +#undef WPTR +#undef DPTR + +
diff --git a/tools/wrc/preproc.c b/tools/wrc/preproc.c new file mode 100644 index 0000000..d523bc5 --- /dev/null +++ b/tools/wrc/preproc.c
@@ -0,0 +1,281 @@ +/* + * Copyright 1998 Bertho A. Stultiens (BS) + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <config.h> +#include "wrc.h" +#include "utils.h" +#include "preproc.h" +#include "parser.h" + + +extern void set_pp_ignore(int); /* From parser.l */ + +static char *current_define; + +#define HASHKEY 2039 +static struct pp_entry *pp_defines[HASHKEY]; + +#define MAXIFSTACK 64 +static struct if_state ifstack[MAXIFSTACK]; +static int ifstackidx = 0; + +#if 0 +void pp_status(void) +{ + int i; + int sum; + int total = 0; + struct pp_entry *ppp; + + printf("Defines statistics:\n"); + for(i = 0; i < HASHKEY; i++) + { + sum = 0; + for(ppp = pp_defines[i]; ppp; ppp = ppp->next) + sum++; + total += sum; + printf("%4d, %3d\n", i, sum); + } + printf("Total defines: %d\n", total); +} +#pragma exit pp_status +#endif + +/* Don't comment on the hash, its primitive but functional... */ +int pp_hash(char *str) +{ + int sum = 0; + while(*str) + sum += *str++; + return sum % HASHKEY; +} + +struct pp_entry *pp_lookup(char *ident) +{ + int index = pp_hash(ident); + struct pp_entry *ppp; + for(ppp = pp_defines[index]; ppp; ppp = ppp->next) + { + if(!strcmp(ident, ppp->ident)) + return ppp; + } + return NULL; +} + +void set_define(char *name) +{ + current_define = xstrdup(name); +} + +void del_define(char *name) +{ + int index; + struct pp_entry *ppp; + + if((ppp = pp_lookup(name)) == NULL) + { + if(pedantic) + yywarning("%s was not defined", name); + return; + } + + index = pp_hash(name); + if(pp_defines[index] == ppp) + { + pp_defines[index] = ppp->next; + if(pp_defines[index]) + pp_defines[index]->prev = NULL; + } + else + { + ppp->prev->next = ppp->next; + if(ppp->next) + ppp->next->prev = ppp->prev; + } + free(ppp); +} + +void add_define(char *text) +{ + int len; + char *cptr; + int index = pp_hash(current_define); + struct pp_entry *ppp; + if(pp_lookup(current_define) != NULL) + { + if(pedantic) + yywarning("Redefinition of %s", current_define); + del_define(current_define); + } + ppp = (struct pp_entry *)xmalloc(sizeof(struct pp_entry)); + ppp->ident = current_define; + ppp->subst = xstrdup(text); + ppp->next = pp_defines[index]; + pp_defines[index] = ppp; + if(ppp->next) + ppp->next->prev = ppp; + /* Strip trailing white space from subst text */ + len = strlen(ppp->subst); + while(len && strchr(" \t\r\n", ppp->subst[len-1])) + { + ppp->subst[--len] = '\0'; + } + /* Strip leading white space from subst text */ + for(cptr = ppp->subst; *cptr && strchr(" \t\r", *cptr); cptr++) + ; + if(ppp->subst != cptr) + memmove(ppp->subst, cptr, strlen(cptr)+1); + if(yydebug) + printf("Added (%s, %d) <%s> to <%s>\n", input_name, line_number, ppp->ident, ppp->subst); +} + +void add_cmdline_define(char *set) +{ + char *cpy = xstrdup(set); /* Because gcc passes a R/O string */ + char *cptr = strchr(cpy, '='); + if(cptr) + *cptr = '\0'; + set_define(cpy); + add_define(cptr ? cptr+1 : ""); + free(cpy); +} + +#if defined(_Windows) || defined(__MSDOS__) +#define INCLUDESEPARATOR ";" +#else +#define INCLUDESEPARATOR ":" +#endif + +static char **includepath; +static int nincludepath = 0; + +void add_include_path(char *path) +{ + char *tok; + char *cpy = xstrdup(path); + + tok = strtok(cpy, INCLUDESEPARATOR); + while(tok) + { + char *dir; + char *cptr; + if(strlen(tok) == 0) + continue; + dir = xstrdup(tok); + for(cptr = dir; *cptr; cptr++) + { + /* Convert to forward slash */ + if(*cptr == '\\') + *cptr = '/'; + } + /* Kill eventual trailing '/' */ + if(*(cptr = dir + strlen(dir)-1) == '/') + *cptr = '\0'; + + /* Add to list */ + nincludepath++; + includepath = (char **)xrealloc(includepath, nincludepath * sizeof(*includepath)); + includepath[nincludepath-1] = dir; + tok = strtok(NULL, INCLUDESEPARATOR); + } + free(cpy); +} + +FILE *open_include(const char *name, int search) +{ + char *cpy = xstrdup(name); + char *cptr; + FILE *fp; + int i; + + for(cptr = cpy; *cptr; cptr++) + { + /* kill double backslash */ + if(*cptr == '\\' && *(cptr+1) == '\\') + memmove(cptr, cptr+1, strlen(cptr)); + /* Convert to forward slash */ + if(*cptr == '\\') + *cptr = '/'; + } + + if(search) + { + /* Search current dir and then -I path */ + fp = fopen(name, "rt"); + if(fp) + { + if(yydebug) + printf("Going to include <%s>\n", name); + free(cpy); + return fp; + } + } + /* Search -I path */ + for(i = 0; i < nincludepath; i++) + { + char *path; + path = (char *)xmalloc(strlen(includepath[i]) + strlen(cpy) + 2); + strcpy(path, includepath[i]); + strcat(path, "/"); + strcat(path, cpy); + fp = fopen(path, "rt"); + if(fp && yydebug) + printf("Going to include <%s>\n", path); + free(path); + if(fp) + { + free(cpy); + return fp; + } + + } + free(cpy); + return NULL; +} + +void push_if(int truecase, int wastrue, int nevertrue) +{ + if(ifstackidx >= MAXIFSTACK-1) + internal_error(__FILE__, __LINE__, "#if stack overflow"); + ifstack[ifstackidx].current = truecase && !wastrue; + ifstack[ifstackidx].hasbeentrue = wastrue; + ifstack[ifstackidx].nevertrue = nevertrue; + if(nevertrue || !(truecase && !wastrue)) + set_pp_ignore(1); + if(yydebug) + printf("push_if: %d %d %d (%d %d %d)\n", + truecase, + wastrue, + nevertrue, + ifstack[ifstackidx].current, + ifstack[ifstackidx].hasbeentrue, + ifstack[ifstackidx].nevertrue); + ifstackidx++; +} + +int pop_if(void) +{ + if(ifstackidx <= 0) + yyerror("#endif without #if|#ifdef|#ifndef (#if stack underflow)"); + ifstackidx--; + if(yydebug) + printf("pop_if: %d %d %d\n", + ifstack[ifstackidx].current, + ifstack[ifstackidx].hasbeentrue, + ifstack[ifstackidx].nevertrue); + if(ifstack[ifstackidx].nevertrue || !ifstack[ifstackidx].current) + set_pp_ignore(0); + return ifstack[ifstackidx].hasbeentrue || ifstack[ifstackidx].current; +} + +int isnevertrue_if(void) +{ + return ifstackidx > 0 && ifstack[ifstackidx-1].nevertrue; +} +
diff --git a/tools/wrc/preproc.h b/tools/wrc/preproc.h new file mode 100644 index 0000000..c8f13f7 --- /dev/null +++ b/tools/wrc/preproc.h
@@ -0,0 +1,35 @@ +/* + * Copyright 1998 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WRC_PREPROC_H +#define __WRC_PREPROC_H + +struct pp_entry { + struct pp_entry *next; + struct pp_entry *prev; + char *ident; + char *subst; + int expanding; +}; + +struct if_state { + int current; + int hasbeentrue; + int nevertrue; +}; + +struct pp_entry *pp_lookup(char *ident); +void set_define(char *name); +void del_define(char *name); +void add_define(char *text); +void add_cmdline_define(char *set); +FILE *open_include(const char *name, int search); +void add_include_path(char *path); +void push_if(int truecase, int wastrue, int nevertrue); +int pop_if(void); +int isnevertrue_if(void); + +#endif +
diff --git a/tools/wrc/readres.c b/tools/wrc/readres.c new file mode 100644 index 0000000..c2fdc6d --- /dev/null +++ b/tools/wrc/readres.c
@@ -0,0 +1,369 @@ +/* + * Read a .res file and create a resource-tree + * + * Copyright 1998 Bertho A. Stultiens + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include <config.h> +#include "wrc.h" +#include "readres.h" +#include "newstruc.h" +#include "utils.h" +#include "genres.h" + +struct resheader32 { + DWORD ressize; /* 0 */ + DWORD hdrsize; /* 0x20 */ + WORD restype1; /* 0xffff */ + WORD restype2; /* 0 */ + WORD resname1; /* 0xffff */ + WORD resname2; /* 0 */ + DWORD dversion; /* 0 */ + WORD memopt; /* 0 */ + WORD language; /* 0 */ + DWORD version; /* 0 */ + DWORD characts; /* 0 */ +} emptyheader = {0, 0x20, 0xffff, 0, 0xffff, 0, 0, 0, 0, 0, 0}; + +/* + ***************************************************************************** + * Function : + * Syntax : + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +/* + ***************************************************************************** + * Function : + * Syntax : + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +/* + ***************************************************************************** + * Function : + * Syntax : + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +int read_data(FILE *fp, size_t size, void *buf) +{ + int r; + int pos = ftell(fp); + r = fread(buf, 1, size, fp); + if(r == size) + return 0; + if(r == 0 && ftell(fp) - pos > 0) + return 1; + else + return -1; +} + +/* + ***************************************************************************** + * Function : + * Syntax : + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +enum res_e res_type_from_id(name_id_t *nid) +{ + if(nid->type == name_str) + return res_usr; + + if(nid->type != name_ord) + internal_error(__FILE__, __LINE__, "Invalid name_id descriptor %d", nid->type); + + switch(nid->name.i_name) + { + case WRC_RT_CURSOR: return res_cur; + case WRC_RT_BITMAP: return res_bmp; + case WRC_RT_ICON: return res_ico; + case WRC_RT_MENU: return res_men; + case WRC_RT_DIALOG: return res_dlg; + case WRC_RT_STRING: return res_stt; + case WRC_RT_FONTDIR: return res_fntdir; + case WRC_RT_FONT: return res_fnt; + case WRC_RT_ACCELERATOR: return res_acc; + case WRC_RT_RCDATA: return res_rdt; + case WRC_RT_MESSAGETABLE: return res_msg; + case WRC_RT_GROUP_CURSOR: return res_curg; + case WRC_RT_GROUP_ICON: return res_icog; + case WRC_RT_VERSION: return res_ver; + + default: + case WRC_RT_DLGINCLUDE: + case WRC_RT_PLUGPLAY: + case WRC_RT_VXD: + case WRC_RT_ANICURSOR: + case WRC_RT_ANIICON: + warning("Cannot be sure of resource type, using usertype settings"); + return res_usr; + } +} + +/* + ***************************************************************************** + * Function : + * Syntax : + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +#define get_word(idx) (*((WORD *)(&res->data[idx]))) +#define get_dword(idx) (*((DWORD *)(&res->data[idx]))) + +resource_t *read_res32(FILE *fp) +{ + static char wrong_format[] = "Wrong resfile format (32bit)"; + DWORD ressize; + DWORD hdrsize; + DWORD totsize; + WORD memopt; + WORD language; + int err; + res_t *res; + resource_t *rsc; + resource_t *tail = NULL; + resource_t *list = NULL; + name_id_t *type = NULL; + name_id_t *name = NULL; + int idx; + enum res_e res_type; + user_t *usrres; + + while(1) + { + /* Get headersize and resource size */ + err = read_data(fp, sizeof(ressize), &ressize); + if(err < 0) + break; + else if(err > 0) + error(wrong_format); + err = read_data(fp, sizeof(hdrsize), &hdrsize); + if(err) + error(wrong_format); + + /* Align sizes and compute total size */ + totsize = hdrsize; + if(hdrsize & 3) + { + warning("Hu? .res header needed alignment (anything can happen now)"); + totsize += 4 - (hdrsize & 3); + } + totsize += ressize; + if(ressize & 3) + totsize += 4 - (ressize & 3); + + /* Read in entire data-block */ + fseek(fp, -8, SEEK_CUR); + res = new_res(); + if(res->allocsize < totsize) + grow_res(res, totsize - res->allocsize + 8); + err = read_data(fp, totsize, res->data); + if(err) + error(wrong_format); + + res->dataidx = hdrsize; + res->size = hdrsize + ressize; + + /* Analyse the content of the header */ + idx = 8; + /* Get restype */ + if(get_word(idx) == 0xffff) + { + idx += sizeof(WORD); + type = new_name_id(); + type->type = name_ord; + type->name.i_name = get_word(idx); + idx += sizeof(WORD); + } + else if(get_word(idx) == 0) + { + error("ResType name has zero length (32 bit)"); + } + else + { + int tag = idx; + string_t *str; + while(1) + { + idx += sizeof(WORD); + if(!get_word(idx)) + break; + } + idx += sizeof(WORD); + str = new_string(); + str->type = str_unicode; + str->size = (idx - tag) / 2; + str->str.wstr = (short *)xmalloc(idx-tag+2); + memcpy(str->str.wstr, &res->data[tag], idx-tag); + str->str.wstr[str->size] = 0; + type = new_name_id(); + type->type = name_str; + type->name.s_name = str; + } + /* Get resname */ + if(get_word(idx) == 0xffff) + { + idx += sizeof(WORD); + name = new_name_id(); + name->type = name_ord; + name->name.i_name = get_word(idx); + idx += sizeof(WORD); + } + else if(get_word(idx) == 0) + { + error("ResName name has zero length (32 bit)"); + } + else + { + int tag = idx; + string_t *str; + while(1) + { + idx += sizeof(WORD); + if(!get_word(idx)) + break; + } + idx += sizeof(WORD); + str = new_string(); + str->type = str_unicode; + str->size = (idx - tag) / 2; + str->str.wstr = (short *)xmalloc(idx-tag+2); + memcpy(str->str.wstr, &res->data[tag], idx-tag); + str->str.wstr[str->size] = 0; + name = new_name_id(); + name->type = name_str; + name->name.s_name = str; + } + + /* align */ + if(idx & 0x3) + idx += 4 - (idx & 3); + + idx += sizeof(DWORD); /* Skip DataVersion */ + memopt = get_word(idx); + idx += sizeof(WORD); + language = get_word(idx); + + /* Build a resource_t list */ + res_type = res_type_from_id(type); + if(res_type == res_usr) + { + /* User-type has custom ResType for .[s|h] generation */ + usrres = new_user(type, NULL, new_int(memopt)); + } + else + usrres = NULL; + rsc = new_resource(res_type, + usrres, + memopt, + new_language(PRIMARYLANGID(language), + SUBLANGID(language))); + rsc->binres = res; + rsc->name = name; + rsc->c_name = make_c_name(get_c_typename(res_type), name, rsc->lan); + if(!list) + { + list = rsc; + tail = rsc; + } + else + { + rsc->prev = tail; + tail->next = rsc; + tail = rsc; + } + } + return list; +} + +/* + ***************************************************************************** + * Function : + * Syntax : + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +resource_t *read_res16(FILE *fp) +{ + internal_error(__FILE__, __LINE__, "Can't yet read 16 bit .res files"); + return NULL; +} + +/* + ***************************************************************************** + * Function : read_resfile + * Syntax : resource_t *read_resfile(char *inname) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +resource_t *read_resfile(char *inname) +{ + FILE *fp; + struct resheader32 rh; + int is32bit; + resource_t *top; + + fp = fopen(inname, "rb"); + if(!fp) + error("Could not open inputfile %s", inname); + + /* Determine 16 or 32 bit .res file */ + if(fread(&rh, 1, sizeof(rh), fp) != sizeof(rh)) + is32bit = 0; + else + { + if(!memcmp(&emptyheader, &rh, sizeof(rh))) + is32bit = 1; + else + is32bit = 0; + } + + if(is32bit && !win32) + error("Cannot convert 32-bit .res-file into 16-bit resources (and will, hopefully never, implement it)"); + + if(!is32bit && win32) + error("Cannot (yet) convert 16-bit .res-file into 32-bit resources"); + + if(!is32bit) + { + fseek(fp, 0, SEEK_SET); + top = read_res16(fp); + } + else + { + top = read_res32(fp); + } + + fclose(fp); + + return top; +}
diff --git a/tools/wrc/readres.h b/tools/wrc/readres.h new file mode 100644 index 0000000..e87da51 --- /dev/null +++ b/tools/wrc/readres.h
@@ -0,0 +1,17 @@ +/* + * Read binary resource prototypes + * + * Copyright 1998 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WRC_READRES_H +#define __WRC_READRES_H + +#ifndef __WRC_WRCTYPES_H +#include "wrctypes.h" +#endif + +resource_t *read_resfile(char *inname); + +#endif
diff --git a/tools/wrc/utils.c b/tools/wrc/utils.c new file mode 100644 index 0000000..fd2832f --- /dev/null +++ b/tools/wrc/utils.c Binary files differ
diff --git a/tools/wrc/utils.h b/tools/wrc/utils.h new file mode 100644 index 0000000..e16613a --- /dev/null +++ b/tools/wrc/utils.h
@@ -0,0 +1,40 @@ +/* + * Utility routines' prototypes etc. + * + * Copyright 1998 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WRC_UTILS_H +#define __WRC_UTILS_H + +#ifndef __WRC_WRCTYPES_H +#include "wrctypes.h" +#endif + +#include <stddef.h> /* size_t */ + +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *str); + +int yyerror(const char *s, ...); +int yywarning(const char *s, ...); +void internal_error(const char *file, int line, const char *s, ...); +void error(const char *s, ...); +void warning(const char *s, ...); +void chat(const char *s, ...); + +char *dup_basename(const char *name, const char *ext); +int string_compare(const string_t *s1, const string_t *s2); +int wstrlen(const short *s); +short *wstrcpy(short *dst, const short *src); +int wstricmp(const short *s1, const short *s2); +char *dupwstr2cstr(const short *str); +short *dupcstr2wstr(const char *str); + +#ifndef HAVE_STRICMP +int stricmp(const char *s1, const char *s2); +#endif + +#endif
diff --git a/tools/wrc/wrc.c b/tools/wrc/wrc.c new file mode 100644 index 0000000..910cf14 --- /dev/null +++ b/tools/wrc/wrc.c
@@ -0,0 +1,492 @@ +/* + * + * Copyright Martin von Loewis, 1994 + * Copyrignt 1998 Bertho A. Stultiens (BS) + * + * 21-May-1998 BS - Removed the CPP option. Its internal now. + * - Added implementations for defines and includes + * on the commandline. + * + * 30-Apr-1998 BS - The options now contain nearly the entire alphabet. + * Seems to be a sign for too much freedom. I implemeted + * most of them as a user choice possibility for things + * that I do not know what to put there by default. + * - -l and -L options are now known as -t and -T. + * + * 23-Apr-1998 BS - Finally gave up on backward compatibility on the + * commandline (after a blessing from the newsgroup). + * So, I changed the lot. + * + * 17-Apr-1998 BS - Added many new command-line options but took care + * that it would not break old scripts (sigh). + * + * 16-Apr-1998 BS - There is not much left of the original source... + * I had to rewrite most of it because the parser + * changed completely with all the types etc.. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> + +#include <config.h> +#include "wrc.h" +#include "utils.h" +#include "writeres.h" +#include "readres.h" +#include "dumpres.h" +#include "genres.h" +#include "newstruc.h" +#include "preproc.h" +#include "parser.h" + +char usage[] = "Usage: wrc [options...] [infile[.rc|.res]]\n" + " -a n Alignment of resource (win16 only, default is 4)\n" + " -b Create a C array from a binary .res file\n" + " -c Add 'const' prefix to C constants\n" + " -C cp Set the resource's codepage to cp (default is 0)\n" + " -d n Set debug level to 'n'\n" + " -D id[=val] Define preprocessor identifier id=val\n" + " -e Disable recognition of win32 keywords in 16bit compile\n" + " -g Add symbols to the global c namespace\n" + " -h Also generate a .h file\n" + " -H file Same as -h but written to file\n" + " -I path Set include search dir to path (multiple -I allowed)\n" + " -l lan Set default language to lan (default is neutral {0})\n" + " -n Do not generate .s file\n" + " -o file Output to file (default is infile.[res|s|h]\n" + " -p prefix Give a prefix for the generated names\n" + " -r Create binary .res file (compile only)\n" + " -s Add structure with win32/16 (PE/NE) resource directory\n" + " -t Generate indirect loadable resource tables\n" + " -T Generate only indirect loadable resources tables\n" + " -V Print version end exit\n" + " -w 16|32 Select win16 or win32 output (default is win32)\n" + " -W Enable pedantic warnings\n" + "Input is taken from stdin if no sourcefile specified.\n" + "Debug level 'n' is a bitmask with following meaning:\n" + " * 0x01 Tell which resource is parsed (verbose mode)\n" + " * 0x02 Dump internal structures\n" + " * 0x04 Create a parser trace (yydebug=1)\n" + "The -o option only applies to the final destination file, which is\n" + "in case of normal compile a .s file. You must use the '-H header.h'\n" + "option to override the header-filename.\n" + "If no input filename is given and the output name is not overridden\n" + "with -o and/or -H, then the output is written to \"wrc.tab.[sh]\"\n" + ; + +char version_string[] = "Wine Resource Compiler Version " WRC_FULLVERSION "\n" + "Copyright 1998 Bertho A. Stultiens\n" + " 1994 Martin von Loewis\n"; + +/* + * Default prefix for resource names used in the C array. + * Option '-p name' sets it to 'name' + */ +char *prefix = "_Resource"; + +/* + * Set if compiling in 32bit mode (default). + */ +int win32 = 1; + +/* + * Set when generated C variables should be prefixed with 'const' + */ +int constant = 0; + +/* + * Create a .res file from the source and exit (-r option). + * The compiler actually always creates this file, and then + * converts it into whatever is requested. The compiler just + * stops after the res-file when create_res is set. + */ +int create_res = 0; + +/* + * debuglevel == DEBUGLEVEL_NONE Don't bother + * debuglevel & DEBUGLEVEL_CHAT Say whats done + * debuglevel & DEBUGLEVEL_DUMP Dump internal structures + * debuglevel & DEBUGLEVEL_TRACE Create parser trace + */ +int debuglevel = DEBUGLEVEL_NONE; + +/* + * Recognize win32 keywords if set (-w 32 enforces this), + * otherwise set with -e option. + */ +int extensions = 1; + +/* + * Set when creating C array from .res file (-b option). + */ +int binary = 0; + +/* + * Set when an additional C-header is to be created in compile (-h option). + */ +int create_header = 0; + +/* + * Set when the NE/PE resource directory should be dumped into + * the output file. + */ +int create_dir = 0; + +/* + * Set when all symbols should be added to the global namespace (-g option) + */ +int global = 0; + +/* + * Set when indirect loadable resource tables should be created (-t) + */ +int indirect = 0; + +/* + * Set when _only_ indirect loadable resource tables should be created (-T) + */ +int indirect_only = 0; + +/* + * NE segment resource aligment (-a option) + */ +int alignment = 4; +int alignment_pwr; + +/* + * Cleared when the assembly file must be suppressed (-n option) + */ +int create_s = 1; + +/* + * Language setting for resources (-l option) + */ +language_t *currentlanguage = NULL; + +/* + * The codepage to write in win32 PE resource segment (-C option) + */ +DWORD codepage = 0; + +/* + * Set when extra warnings should be generated (-W option) + */ +int pedantic = 0; + +char *output_name; /* The name given by the -o option */ +char *input_name; /* The name given on the command-line */ +char *header_name; /* The name given by the -H option */ + +char *cmdline; /* The entire commandline */ + +resource_t *resource_top; /* The top of the parsed resources */ + +int getopt (int argc, char *const *argv, const char *optstring); + +int main(int argc,char *argv[]) +{ + extern char* optarg; + extern int optind; + int optc; + int lose = 0; + int ret; + int a; + int i; + int cmdlen; + + /* First rebuild the commandline to put in destination */ + /* Could be done through env[], but not all OS-es support it */ + cmdlen = 4; /* for "wrc " */ + for(i = 1; i < argc; i++) + cmdlen += strlen(argv[i]) + 1; + cmdline = (char *)xmalloc(cmdlen); + strcpy(cmdline, "wrc "); + for(i = 1; i < argc; i++) + { + strcat(cmdline, argv[i]); + if(i < argc-1) + strcat(cmdline, " "); + } + + while((optc = getopt(argc, argv, "a:bcC:d:D:eghH:I:l:no:p:rstTVw:W")) != EOF) + { + switch(optc) + { + case 'a': + alignment = atoi(optarg); + break; + case 'b': + binary = 1; + break; + case 'c': + constant = 1; + break; + case 'C': + codepage = strtol(optarg, NULL, 0); + break; + case 'd': + debuglevel = strtol(optarg, NULL, 0); + break; + case 'D': + add_cmdline_define(optarg); + break; + case 'e': + extensions = 0; + break; + case 'g': + global = 1; + break; + case 'H': + header_name = strdup(optarg); + /* Fall through */ + case 'h': + create_header = 1; + break; + case 'I': + add_include_path(optarg); + break; + case 'l': + { + int lan; + lan = strtol(optarg, NULL, 0); + currentlanguage = new_language(PRIMARYLANGID(lan), SUBLANGID(lan)); + } + break; + case 'n': + create_s = 0; + break; + case 'o': + output_name = strdup(optarg); + break; + case 'p': + prefix = strdup(optarg); + break; + case 'r': + create_res = 1; + break; + case 's': + create_dir = 1; + break; + case 'T': + indirect_only = 1; + /* Fall through */ + case 't': + indirect = 1; + break; + case 'V': + printf(version_string); + exit(0); + break; + case 'w': + if(!strcmp(optarg, "16")) + win32 = 0; + else if(!strcmp(optarg, "32")) + win32 = 1; + else + lose++; + break; + case 'W': + pedantic = 1; + break; + default: + lose++; + break; + } + } + + if(lose) + { + fprintf(stderr, usage); + return 1; + } + + /* Check the command line options for invalid combinations */ + if(win32) + { + if(!extensions) + { + warning("Option -e ignored with 32bit compile\n"); + extensions = 1; + } + } + + if(create_res) + { + if(constant) + { + warning("Option -c ignored with compile to .res\n"); + constant = 0; + } + + if(create_header) + { + warning("Option -[h|H] ignored with compile to .res\n"); + create_header = 0; + } + + if(indirect) + { + warning("Option -l ignored with compile to .res\n"); + indirect = 0; + } + + if(indirect_only) + { + warning("Option -L ignored with compile to .res\n"); + indirect_only = 0; + } + + if(global) + { + warning("Option -g ignored with compile to .res\n"); + global = 0; + } + + if(create_dir) + { + error("Option -r and -s cannot be used together\n"); + } + + if(binary) + { + error("Option -r and -b cannot be used together\n"); + } + } + + /* Set alignment power */ + a = alignment; + for(alignment_pwr = 0; alignment_pwr < 10 && a > 1; alignment_pwr++) + { + a >>= 1; + } + if(a != 1) + { + error("Alignment must be between 1 and 1024"); + } + if((1 << alignment_pwr) != alignment) + { + error("Alignment must be a power of 2"); + } + + /* Kill io buffering when some kind of debuglevel is enabled */ + if(debuglevel) + { + setbuf(stdout,0); + setbuf(stderr,0); + } + + yydebug = debuglevel & DEBUGLEVEL_TRACE ? 1 : 0; + + /* Set the default defined stuff */ + add_cmdline_define("RC_INVOKED=1"); + add_cmdline_define("__WRC__=1"); + if(win32) + { + add_cmdline_define("__WIN32__=1"); + add_cmdline_define("__FLAT__=1"); + } + + /* Check if the user set a language, else set default */ + if(!currentlanguage) + currentlanguage = new_language(0, 0); + + /* Check for input file on command-line */ + if(optind < argc) + { + input_name = argv[optind]; + yyin = fopen(input_name, "rb"); + if(!yyin) + { + error("Could not open %s\n", input_name); + } + } + else + { + yyin = stdin; + } + + if(binary && !input_name) + { + error("Binary mode requires .res file as input"); + } + + if(!output_name) + { + output_name = dup_basename(input_name, binary ? ".res" : ".rc"); + strcat(output_name, create_res ? ".res" : ".s"); + } + + if(!header_name && !create_res) + { + header_name = dup_basename(input_name, binary ? ".res" : ".rc"); + strcat(header_name, ".h"); + } + + if(!binary) + { + /* Go from .rc to .res or .s */ + chat("Starting parse"); + ret = yyparse(); + + if(input_name) + fclose(yyin); + + if(ret) + { + /* Error during parse */ + exit(1); + } + + if(debuglevel & DEBUGLEVEL_DUMP) + dump_resources(resource_top); + + /* Convert the internal lists to binary data */ + resources2res(resource_top); + + if(create_res) + { + chat("Writing .res-file"); + write_resfile(output_name, resource_top); + } + else + { + if(create_s) + { + chat("Writing .s-file"); + write_s_file(output_name, resource_top); + } + if(create_header) + { + chat("Writing .h-file"); + write_h_file(header_name, resource_top); + } + } + + } + else + { + /* Go from .res to .s */ + chat("Reading .res-file"); + resource_top = read_resfile(input_name); + if(create_s) + { + chat("Writing .s-file"); + write_s_file(output_name, resource_top); + } + if(create_header) + { + chat("Writing .h-file"); + write_h_file(header_name, resource_top); + } + } + + return 0; +} + + + +
diff --git a/tools/wrc/wrc.doc b/tools/wrc/wrc.doc new file mode 100644 index 0000000..94846aa --- /dev/null +++ b/tools/wrc/wrc.doc
@@ -0,0 +1,174 @@ + + +/*****************************************************************************\ + * Win32 Resource files grammar: + * + * Top level keywords/resource items: + * nameID ACCELERATOR + * [CHARACTERISTICS dword] + * [LANGUAGE language, sublanguage] + * [VERSION dword] + * BEGIN + * event, IDvalue [, type] [options] + * ... + * END + *------------- + * nameID BITMAP [loadmem] filename + *------------- + * nameID BITMAP [loadmem] + * BEGIN + * rawdata + * END + *------------- + * nameID CURSOR [loadmem] filename + *------------- + * nameID CURSOR [loadmem] + * BEGIN + * rawdata + * END + *------------- + * nameID DIALOG [loadmem] [EXSTYLE=extended-styles] x, y, width, height + * [CAPTION "text"] + * [CHARACTERISTICS dword] + * [CLASS class] + * [EXSTYLE=extended-styles] + * [FONT pointsize, face] + * [LANGUAGE language, sublanguage] + * [MENU menuname] + * [STYLE styles] + * [VERSION dword] + * BEGIN + * [controls] + * END + *------------- + * nameID DIALOGEX [loadmem] x, y, width, height [, helpID] + * [CAPTION "text"] + * [CHARACTERISTICS dword] + * [CLASS class] + * [EXSTYLE=extended-styles] + * [FONT pointsize, face, weight, italic] + * [LANGUAGE language, sublanguage] + * [MENU menuname] + * [STYLE styles] + * [VERSION dword] + * BEGIN + * [controls] + * END + *------------- + * nameID FONT [loadmem] filename + *------------- + * nameID ICON [loadmem] filename + *------------- + * nameID ICON [loadmem] + * BEGIN + * rawdata + * END + *------------- + * LANGUAGE languageID, sublanguageID + *------------- + * nameID MENU [loadmem] + * [CHARACTERISTICS dword] + * [LANGUAGE language, sublanguage] + * [VERSION dword] + * BEGIN + * [menuitems] + * END + *------------- + * nameID MENUEX [loadmem] + * [CHARACTERISTICS dword] + * [LANGUAGE language, sublanguage] + * [VERSION dword] + * BEGIN + * [menuexitems] + * END + *------------- + * nameID MESSAGETABLE [loadmem] filename + *------------- + * nameID RCDATA [loadmem] + * [CHARACTERISTICS dword] + * [LANGUAGE language, sublanguage] + * [VERSION dword] + * BEGIN + * [raw-data] + * END + *------------- + * STRINGTABLE [loadmem] + * [CHARACTERISTICS dword] + * [LANGUAGE language, sublanguage] + * [VERSION dword] + * BEGIN + * [stringID, "text"] + * ... + * END + *------------- + * versionID VERSIONINFO + * FILEVERSION maj1, maj2, min1, min2 + * PRODUCTVERSION maj1, maj2, min1, min2 + * FILEFLAGSMASK (VS_FFI_FILEFLAGMASK) + * FILEOS (VOS_*) + * FILETYPE (VFT_*) + * FILEFLAGS (VS_FF_*) + * FILESUBTYPE (VFT2_*) + * BEGIN + * BLOCK "StringFileInfo" + * BEGIN + * BLOCK "language-charset" + * BEGIN + * [VALUE "Comments", "yep\0"] + * VALUE "CompanyName", "Wine Developer Team\0" + * VALUE "FileDescription", "Resource compiler\0" + * VALUE "FileVersion", "0.01 alpha\0" + * VALUE "InternalName", "winerc\0" + * [VALUE "LegalCopyright", "Copyright (c) B.A. Stultiens 1998\0"] + * [VALUE "LegalTrademarks", "...\0"] + * VALUE "OriginalFilename", "winerc\0" + * [VALUE "PrivateBuild", "Yes always!\0" + * VALUE "ProductName", "Wine\0" + * VALUE "ProductVersion", "0.01 alpha\0" + * [VALUE "SpecialBuild", "No never! Well, maybe.\0" + * END + * END + * BLOCK "VarFileInfo" + * BEGIN + * VALUE "Translation", languageID, charsetID + * END + * END + *------------- + * nameID typeID [loadmem] filename + *------------- + * nameID typeID [loadmem] + * BEGIN + * [raw-data] + * END + * + ***************************************************************************** + * + * Dialog controls: + * CONTROL text, ID, class, style, x, y, width, height [, exstyle] + * + * LTEXT text, ID, x, y, width, height [, style [, exstyle]] + * CTEXT text, ID, x, y, width, height [, style [, exstyle]] + * RTEXT text, ID, x, y, width, height [, style [, exstyle]] + * + * ICON text, ID, x, y, [width, height , style [, exstyle]] + * + * EDITTEXT ID, x, y, width, height [, style [, exstyle]] + * + * AUTO3STATE text, ID, x, y, width, height [, style [, exstyle]] + * AUTOCHECKBOX text, ID, x, y, width, height [, style [, exstyle]] + * AUTORADIOBUTTON text, ID, x, y, width, height [, style [, exstyle]] + * STATE3 text, ID, x, y, width, height [, style [, exstyle]] + * CHECKBOX text, ID, x, y, width, height [, style [, exstyle]] + * PUSHBUTTON text, ID, x, y, width, height [, style [, exstyle]] + * RADIOBUTTON text, ID, x, y, width, height [, style [, exstyle]] + * DEFPUSHBUTTON text, ID, x, y, width, height [, style [, exstyle]] + * + * COMBOBOX text, ID, x, y, width, height [, style [, exstyle]] + * GROUPBOX text, ID, x, y, width, height [, style [, exstyle]] + * LISTBOX text, ID, x, y, width, height [, style [, exstyle]] + * SCROLLBAR text, ID, x, y, width, height [, style [, exstyle]] + * + * FIXME: This is documented, but I don't know the BS_PUSHBOX style + * PUSHBOX text, ID, x, y, width, height [, style [, exstyle]] +\*****************************************************************************/ +
diff --git a/tools/wrc/wrc.h b/tools/wrc/wrc.h new file mode 100644 index 0000000..13ad29e --- /dev/null +++ b/tools/wrc/wrc.h
@@ -0,0 +1,89 @@ +/* + * Main definitions and externals + * + * Copyright 1998 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WRC_WRC_H +#define __WRC_WRC_H + +#ifndef __WRC_WRCTYPES_H +#include "wrctypes.h" +#endif + +#define WRC_VERSION "1.0.0" +#define WRC_RELEASEDATE "(28-May-1998)" +#define WRC_FULLVERSION WRC_VERSION " " WRC_RELEASEDATE + +/* Only used in heavy debugging sessions */ +#define HEAPCHECK() + +/* Memory/load flags */ +#define WRC_MO_MOVEABLE 0x0010 +#define WRC_MO_PURE 0x0020 +#define WRC_MO_PRELOAD 0x0040 +#define WRC_MO_DISCARDABLE 0x1000 + +/* Resource type IDs */ +#define WRC_RT_CURSOR (1) +#define WRC_RT_BITMAP (2) +#define WRC_RT_ICON (3) +#define WRC_RT_MENU (4) +#define WRC_RT_DIALOG (5) +#define WRC_RT_STRING (6) +#define WRC_RT_FONTDIR (7) +#define WRC_RT_FONT (8) +#define WRC_RT_ACCELERATOR (9) +#define WRC_RT_RCDATA (10) +#define WRC_RT_MESSAGETABLE (11) +#define WRC_RT_GROUP_CURSOR (12) +#define WRC_RT_GROUP_ICON (14) +#define WRC_RT_VERSION (16) +#define WRC_RT_DLGINCLUDE (17) +#define WRC_RT_PLUGPLAY (19) +#define WRC_RT_VXD (20) +#define WRC_RT_ANICURSOR (21) +#define WRC_RT_ANIICON (22) + +/* Default class type IDs */ +#define CT_BUTTON 0x80 +#define CT_EDIT 0x81 +#define CT_STATIC 0x82 +#define CT_LISTBOX 0x83 +#define CT_SCROLLBAR 0x84 +#define CT_COMBOBOX 0x85 + +/* From wrc.c */ +extern int debuglevel; +#define DEBUGLEVEL_NONE 0x0000 +#define DEBUGLEVEL_CHAT 0x0001 +#define DEBUGLEVEL_DUMP 0x0002 +#define DEBUGLEVEL_TRACE 0x0004 + +extern int win32; +extern int constant; +extern int create_res; +extern int extensions; +extern int binary; +extern int create_header; +extern int create_dir; +extern int global; +extern int indirect; +extern int indirect_only; +extern int alignment; +extern int alignment_pwr; +extern int create_s; +extern DWORD codepage; +extern int pedantic; + +extern char *prefix; +extern char *output_name; +extern char *input_name; +extern char *header_name; +extern char *cmdline; + +extern resource_t *resource_top; +extern language_t *currentlanguage; + +#endif
diff --git a/tools/wrc/wrctypes.h b/tools/wrc/wrctypes.h new file mode 100644 index 0000000..00075de --- /dev/null +++ b/tools/wrc/wrctypes.h
@@ -0,0 +1,467 @@ +/* + * General type definitions + * + * Copyright 1998 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WRC_WRCTYPES_H +#define __WRC_WRCTYPES_H + +/* First is MS style, second wine style */ +#if !defined(_INC_WINDOWS) && !defined(__WINE_WINDOWS_H) +#include "windows.h" +#endif + +#ifndef MAKELANGID +#include "winnls.h" +#endif + +#ifndef VS_FFI_SIGNATURE +#include "ver.h" +#endif + + +/* Binary resource structure */ +#define RES_BLOCKSIZE 512 + +typedef struct res { + int allocsize; /* Allocated datablock size */ + int size; /* Actual size of data */ + int dataidx; /* Tag behind the resource-header */ + char *data; +} res_t; + +/* Resource strings are slightly more complex because they include '\0' */ +enum str_e {str_char, str_unicode}; + +typedef struct string { + int size; + enum str_e type; + union { + char *cstr; + short *wstr; + } str; +} string_t; + +/* Resources are identified either by name or by number */ +enum name_e {name_str, name_ord}; + +typedef struct name_id { + union { + string_t *s_name; + int i_name; + } name; + enum name_e type; +} name_id_t; + +/* Language definitions */ +typedef struct language { + int id; + int sub; +} language_t; + +typedef DWORD characts_t; +typedef DWORD version_t; + +typedef struct lvc { + language_t *language; + version_t *version; + characts_t *characts; +} lvc_t; + +typedef struct font_id { + string_t *name; + int size; + int weight; + int italic; +} font_id_t; + +/* resource types */ +/* These are in the same order (and ordinal) as the RT_xxx + * defines. This is _required_. + * I rolled my own numbers for the win32 extension that are + * documented, but generate either old RT_xxx numbers, or + * don't have an ordinal associated (user type). + * I don't know any specs for those noted such, for that matter, + * I don't even know whether they can be generated other than by + * using a user-type resource. + */ +enum res_e { + res_0 = 0, + res_cur, + res_ico, + res_bmp, + res_men, + res_dlg, + res_stt, + res_fntdir, + res_fnt, + res_acc, + res_rdt, + res_msg, + res_curg, + res_13, /* Hm, wonder why its not used... */ + res_icog, + res_15, + res_ver, + res_dlginc, /* Not implemented, no layout available */ + res_18, + res_pnp, /* Not implemented, no layout available */ + res_vxd, /* Not implemented, no layout available */ + res_anicur, /* Not implemented, no layout available */ + res_aniico, /* Not implemented, no layout available */ + + res_menex = 256 + 4, + res_dlgex, + res_usr, +}; + +/* Raw bytes in a row... */ +typedef struct raw_data { + int size; + char *data; +} raw_data_t; + +/* Dialog structures */ +typedef struct control { + struct control *next; /* List of controls */ + struct control *prev; + name_id_t *ctlclass; /* ControlClass */ + string_t *title; /* Title of control */ + int id; + int x; /* Position */ + int y; + int width; /* Size */ + int height; + DWORD style; /* Style */ + DWORD exstyle; + DWORD helpid; /* EX: */ + int gotstyle; /* Used to determine whether the default */ + int gotexstyle; /* styles must be set */ + int gothelpid; + raw_data_t *extra; /* EX: number of extra bytes in resource */ +} control_t; + +typedef struct dialog { + DWORD memopt; + int x; /* Position */ + int y; + int width; /* Size */ + int height; + DWORD style; /* Style */ + DWORD exstyle; + int gotstyle; /* Used to determine whether the default */ + int gotexstyle; /* styles must be set */ + name_id_t *menu; + name_id_t *dlgclass; + string_t *title; + font_id_t *font; + lvc_t lvc; + control_t *controls; +} dialog_t; + +/* DialogEx structures */ +typedef struct dialogex { + DWORD memopt; + int x; /* Position */ + int y; + int width; /* Size */ + int height; + DWORD style; /* Style */ + DWORD exstyle; + DWORD helpid; /* EX: */ + int gotstyle; /* Used to determine whether the default */ + int gotexstyle; /* styles must be set */ + int gothelpid; + name_id_t *menu; + name_id_t *dlgclass; + string_t *title; + font_id_t *font; + lvc_t lvc; + control_t *controls; +} dialogex_t; + +/* Menu structures */ +typedef struct menu_item { + struct menu_item *next; + struct menu_item *prev; + struct menu_item *popup; + int id; + DWORD state; + string_t *name; +} menu_item_t; + +typedef struct menu { + DWORD memopt; + lvc_t lvc; + menu_item_t *items; +} menu_t; + +/* MenuEx structures */ +typedef struct menuex_item { + struct menuex_item *next; + struct menuex_item *prev; + struct menuex_item *popup; + int id; + DWORD type; + DWORD state; + int helpid; + string_t *name; + int gotid; + int gottype; + int gotstate; + int gothelpid; +} menuex_item_t; + +typedef struct menuex { + DWORD memopt; + lvc_t lvc; + menuex_item_t *items; +} menuex_t; + +typedef struct itemex_opt +{ + int id; + DWORD type; + DWORD state; + int helpid; + int gotid; + int gottype; + int gotstate; + int gothelpid; +} itemex_opt_t; + +/* RC structures for types read from file or supplied binary */ +typedef struct font { + DWORD memopt; + raw_data_t *data; +} font_t; + +typedef struct icon_dir_entry { + BYTE width; /* From the SKD doc. */ + BYTE height; + BYTE nclr; + BYTE reserved; + WORD planes; + WORD bits; + DWORD ressize; + DWORD offset; +} icon_dir_entry_t; + +typedef struct icon { + struct icon *next; + struct icon *prev; + int id; /* Unique icon id within resource file */ + int width; /* Field from the IconDirEntry */ + int height; + int nclr; + int planes; + int bits; + raw_data_t *data; +} icon_t; + +typedef struct icon_group { + DWORD memopt; + icon_t *iconlist; + int nicon; +} icon_group_t; + +typedef struct cursor_dir_entry { + BYTE width; /* From the SKD doc. */ + BYTE height; + BYTE nclr; + BYTE reserved; + WORD xhot; + WORD yhot; + DWORD ressize; + DWORD offset; +} cursor_dir_entry_t; + +typedef struct cursor { + struct cursor *next; + struct cursor *prev; + int id; /* Unique icon id within resource file */ + int width; /* Field from the CursorDirEntry */ + int height; + int nclr; + int planes; + int bits; + int xhot; + int yhot; + raw_data_t *data; +} cursor_t; + +typedef struct cursor_group { + DWORD memopt; + cursor_t *cursorlist; + int ncursor; +} cursor_group_t; + +typedef struct bitmap { + DWORD memopt; + raw_data_t *data; +} bitmap_t; + +typedef struct rcdata { + DWORD memopt; + lvc_t lvc; + raw_data_t *data; +} rcdata_t; + +typedef struct user { + DWORD memopt; + name_id_t *type; + raw_data_t *data; +} user_t; + +typedef struct messagetable { + raw_data_t *data; +} messagetable_t; + +/* StringTable structures */ +typedef struct stt_entry { + string_t *str; + int id; + DWORD memopt; + characts_t *characts; + version_t *version; +} stt_entry_t; + +typedef struct stringtable { + struct stringtable *next; + struct stringtable *prev; + DWORD memopt; + lvc_t lvc; + int idbase; + int nentries; + stt_entry_t *entries; +} stringtable_t; + +/* VersionInfo structures */ +enum ver_val_e {val_str, val_words, val_block}; + +struct ver_block; /* Forward ref */ + +typedef struct ver_words { + WORD *words; + int nwords; +} ver_words_t; + +typedef struct ver_value { + struct ver_value *next; + struct ver_value *prev; + string_t *key; + union { + string_t *str; + ver_words_t *words; + struct ver_block *block; + } value; + enum ver_val_e type; +} ver_value_t; + +typedef struct ver_block { + struct ver_block *next; + struct ver_block *prev; + string_t *name; + ver_value_t *values; +} ver_block_t; + +typedef struct versioninfo { + int filever_maj1; + int filever_maj2; + int filever_min1; + int filever_min2; + int prodver_maj1; + int prodver_maj2; + int prodver_min1; + int prodver_min2; + int fileos; + int fileflags; + int fileflagsmask; + int filetype; + int filesubtype; + struct { + int fv:1; + int pv:1; + int fo:1; + int ff:1; + int ffm:1; + int ft:1; + int fst:1; + } gotit; + ver_block_t *blocks; +} versioninfo_t; + +/* Accelerator structures */ +#define WRC_AF_VIRTKEY 0x0001 +#define WRC_AF_NOINVERT 0x0002 +#define WRC_AF_SHIFT 0x0004 +#define WRC_AF_CONTROL 0x0008 +#define WRC_AF_ALT 0x0010 +#define WRC_AF_ASCII 0x4000 + +typedef struct event { + struct event *next; + struct event *prev; + int flags; + int key; + int id; +} event_t; + +typedef struct accelerator { + DWORD memopt; + lvc_t lvc; + event_t *events; +} accelerator_t; + +/* A top-level resource node */ +typedef struct resource { + struct resource *next; + struct resource *prev; + enum res_e type; + name_id_t *name; /* resource's name */ + language_t *lan; /* Only used as a sorting key and c-name creation*/ + union { + accelerator_t *acc; + bitmap_t *bmp; + cursor_t *cur; + cursor_group_t *curg; + dialog_t *dlg; + dialogex_t *dlgex; + font_t *fnt; + icon_t *ico; + icon_group_t *icog; + menu_t *men; + menuex_t *menex; + messagetable_t *msg; + rcdata_t *rdt; + stringtable_t *stt; + user_t *usr; + versioninfo_t *ver; + void *overlay; /* To catch all types at once... */ + } res; + res_t *binres; /* To binary converted resource */ + char *c_name; /* BaseName in output */ + DWORD memopt; +} resource_t; + +/* Resource count */ +typedef struct res32_count { + int count; + resource_t **rsc; +} res32_count_t; + +typedef struct res_count { + name_id_t type; + int count; /* win16 mode */ + resource_t **rscarray; + int count32; + res32_count_t *rsc32array; /* win32 mode */ + int n_id_entries; + int n_name_entries; +} res_count_t; + +#endif + +
diff --git a/tools/wrc/writeres.c b/tools/wrc/writeres.c new file mode 100644 index 0000000..18084f0 --- /dev/null +++ b/tools/wrc/writeres.c
@@ -0,0 +1,1134 @@ +/* + * Write .res, .s and .h file(s) from a resource-tree + * + * Copyright 1998 Bertho A. Stultiens + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <time.h> + +#include <config.h> +#include "wrc.h" +#include "writeres.h" +#include "genres.h" +#include "newstruc.h" +#include "utils.h" + +char s_file_head_str[] = + "#\n" + "# This file is generated with wrc version " WRC_FULLVERSION ". Do not edit!\n" + "# Source : %s\n" + "# Cmdline: %s\n" + "# Date : %s" + "#\n\n" + "\t.data\n\n" + ; + +char s_file_tail_str[] = + "# <eof>\n\n" + ; + +char h_file_head_str[] = + "/*\n" + " * This file is generated with wrc version " WRC_FULLVERSION ". Do not edit!\n" + " * Source : %s\n" + " * Cmdline: %s\n" + " * Date : %s" + " */\n\n" + "#ifndef __%08x_H\n" /* This becomes the data of compile */ + "#define __%08x_H\n\n" + "#ifndef __WRC_RSC_H\n" + "#include <wrc_rsc.h>\n" + "#endif\n\n" + ; + +char h_file_tail_str[] = + "#endif\n" + "/* <eof> */\n\n" + ; + +char _NEResTab[] = "_NEResTab"; +char _PEResTab[] = "_PEResTab"; +char _ResTable[] = "_ResTable"; + +res_count_t *rcarray = NULL; +int rccount = 0; +int n_id_entries = 0; +int n_name_entries = 0; + +time_t now; + +/* + ***************************************************************************** + * Function : write_resfile + * Syntax : void write_resfile(char *outname, resource_t *top) + * Input : + * outname - Filename to write to + * top - The resource-tree to convert + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void write_resfile(char *outname, resource_t *top) +{ + FILE *fo; + int ret; + char zeros[3] = {0, 0, 0}; + + fo = fopen(outname, "wb"); + if(!fo) + { + error("Could not open %s\n", outname); + } + + if(win32) + { + /* Put an empty resource first to signal win32 format */ + res_t *res = new_res(); + put_dword(res, 0); /* ResSize */ + put_dword(res, 0x00000020); /* HeaderSize */ + put_word(res, 0xffff); /* ResType */ + put_word(res, 0); + put_word(res, 0xffff); /* ResName */ + put_word(res, 0); + put_dword(res, 0); /* DataVersion */ + put_word(res, 0); /* Memory options */ + put_word(res, 0); /* Language */ + put_dword(res, 0); /* Version */ + put_dword(res, 0); /* Charateristics */ + ret = fwrite(res->data, 1, res->size, fo); + if(ret != res->size) + { + fclose(fo); + error("Error writing %s", outname); + } + free(res); + } + + for(; top; top = top->next) + { + if(!top->binres) + continue; + + ret = fwrite(top->binres->data, 1, top->binres->size, fo); + if(ret != top->binres->size) + { + fclose(fo); + error("Error writing %s", outname); + } + if(win32 && (top->binres->size & 0x03)) + { + /* Write padding */ + ret = fwrite(zeros, 1, 4 - (top->binres->size & 0x03), fo); + if(ret != 4 - (top->binres->size & 0x03)) + { + fclose(fo); + error("Error writing %s", outname); + } + } + } + fclose(fo); +} + +/* + ***************************************************************************** + * Function : write_s_res + * Syntax : void write_s_res(FILE *fp, res_t *res) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +#define BYTESPERLINE 8 +void write_s_res(FILE *fp, res_t *res) +{ + int idx = res->dataidx; + int end = res->size; + int rest = (end - idx) % BYTESPERLINE; + int lines = (end - idx) / BYTESPERLINE; + int i, j; + + for(i = 0 ; i < lines; i++) + { + fprintf(fp, "\t.byte\t"); + for(j = 0; j < BYTESPERLINE; j++, idx++) + { + fprintf(fp, "0x%02x%s", res->data[idx] & 0xff, + j == BYTESPERLINE-1 ? "" : ", "); + } + fprintf(fp, "\n"); + } + if(rest) + { + fprintf(fp, "\t.byte\t"); + for(j = 0; j < rest; j++, idx++) + { + fprintf(fp, "0x%02x%s", res->data[idx] & 0xff, + j == rest-1 ? "" : ", "); + } + fprintf(fp, "\n"); + } +} + +/* + ***************************************************************************** + * Function : write_name_str + * Syntax : void write_name_str(FILE *fp, name_id_t *nid) + * Input : + * Output : + * Description : + * Remarks : One level self recursive for string type conversion + ***************************************************************************** +*/ +void write_name_str(FILE *fp, name_id_t *nid) +{ + res_t res; + assert(nid->type == name_str); + + if(!win32 && nid->name.s_name->type == str_char) + { + res.size = strlen(nid->name.s_name->str.cstr) + 1; + if(res.size > 254) + error("Can't write strings larger than 254 bytes"); + if(res.size == 0) + internal_error(__FILE__, __LINE__, "Attempt to write empty string"); + res.dataidx = 0; + res.data = (char *)xmalloc(res.size); + res.data[0] = (char)res.size; + strcpy(res.data+1, nid->name.s_name->str.cstr); + write_s_res(fp, &res); + free(res.data); + } + else if(!win32 && nid->name.s_name->type == str_unicode) + { + name_id_t lnid; + string_t str; + + lnid.type = name_str; + lnid.name.s_name = &str; + str.type = str_char; + str.str.cstr = dupwstr2cstr(nid->name.s_name->str.wstr); + write_name_str(fp, &lnid); + free(str.str.cstr); + } + else if(win32 && nid->name.s_name->type == str_char) + { + name_id_t lnid; + string_t str; + + lnid.type = name_str; + lnid.name.s_name = &str; + str.type = str_unicode; + str.str.wstr = dupcstr2wstr(nid->name.s_name->str.cstr); + write_name_str(fp, &lnid); + free(str.str.wstr); + } + else if(win32 && nid->name.s_name->type == str_unicode) + { + res.size = wstrlen(nid->name.s_name->str.wstr) + 1; + if(res.size > 65534) + error("Can't write strings larger than 65534 bytes"); + if(res.size == 0) + internal_error(__FILE__, __LINE__, "Attempt to write empty string"); + res.dataidx = 0; + res.data = (char *)xmalloc(res.size * 2); + ((short *)res.data)[0] = (char)res.size; + wstrcpy((short *)(res.data+2), nid->name.s_name->str.wstr); + res.size *= 2; /* Function writes bytes, not shorts... */ + write_s_res(fp, &res); + free(res.data); + } + else + { + internal_error(__FILE__, __LINE__, "Hmm, requested to write a string of unknown type %d", + nid->name.s_name->type); + } +} + +/* + ***************************************************************************** + * Function : compare_name_id + * Syntax : int compare_name_id(name_id_t *n1, name_id_t *n2) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +int compare_name_id(name_id_t *n1, name_id_t *n2) +{ + if(n1->type == name_ord && n2->type == name_ord) + { + return n1->name.i_name - n2->name.i_name; + } + else if(n1->type == name_str && n2->type == name_str) + { + if(n1->name.s_name->type == str_char + && n2->name.s_name->type == str_char) + { + return stricmp(n1->name.s_name->str.cstr, n2->name.s_name->str.cstr); + } + else if(n1->name.s_name->type == str_unicode + && n2->name.s_name->type == str_unicode) + { + return wstricmp(n1->name.s_name->str.wstr, n2->name.s_name->str.wstr); + } + else + { + internal_error(__FILE__, __LINE__, "Can't yet compare strings of mixed type"); + } + } + else if(n1->type == name_ord && n2->type == name_str) + return -1; + else if(n1->type == name_str && n2->type == name_ord) + return 1; + else + internal_error(__FILE__, __LINE__, "Comparing name-ids with unknown types (%d, %d)", + n1->type, n2->type); + + return 0; /* Keep the compiler happy */ +} + +/* + ***************************************************************************** + * Function : find_counter + * Syntax : res_count_t *find_counter(name_id_t *type) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +res_count_t *find_counter(name_id_t *type) +{ + int i; + for(i = 0; i < rccount; i++) + { + if(!compare_name_id(type, &(rcarray[i].type))) + return &rcarray[i]; + } + return NULL; +} + +/* + ***************************************************************************** + * Function : count_resources + * Syntax : res_count_t *count_resources(resource_t *top) + * Input : + * Output : + * Description : + * Remarks : The whole lot is converted into arrays because they are + * easy sortable. Makes the lot almost unreadable, but it + * works (I hope). Basically you have to keep in mind that + * the lot is a three-dimensional structure for win32 and a + * two-dimensional structure for win16. + ***************************************************************************** +*/ +#define RCT(v) (*((resource_t **)(v))) +/* qsort sorting function */ +int sort_name_id(const void *e1, const void *e2) +{ + return compare_name_id(RCT(e1)->name, RCT(e2)->name); +} + +int sort_language(const void *e1, const void *e2) +{ + assert((RCT(e1)->lan) != NULL); + assert((RCT(e2)->lan) != NULL); + + return MAKELANGID(RCT(e1)->lan->id, RCT(e1)->lan->sub) + - MAKELANGID(RCT(e2)->lan->id, RCT(e2)->lan->sub); +} +#undef RCT +#define RCT(v) ((res_count_t *)(v)) +int sort_type(const void *e1, const void *e2) +{ + return compare_name_id(&(RCT(e1)->type), &(RCT(e2)->type)); +} +#undef RCT + +void count_resources(resource_t *top) +{ + resource_t *rsc; + res_count_t *rcp; + name_id_t nid; + int i, j; + + for(rsc = top; rsc; rsc = rsc->next) + { + if(!rsc->binres) + continue; + switch(rsc->type) + { + case res_dlgex: + nid.name.i_name = WRC_RT_DIALOG; + nid.type = name_ord; + break; + case res_menex: + nid.name.i_name = WRC_RT_MENU; + nid.type = name_ord; + break; + case res_usr: + nid = *(rsc->res.usr->type); + break; + default: + nid.name.i_name = rsc->type; + nid.type = name_ord; + } + + if((rcp = find_counter(&nid)) == NULL) + { + /* Count the number of uniq ids and names */ + + if(nid.type == name_ord) + n_id_entries++; + else + n_name_entries++; + + if(!rcarray) + { + rcarray = (res_count_t *)xmalloc(sizeof(res_count_t)); + rccount = 1; + rcarray[0].count = 1; + rcarray[0].type = nid; + rcarray[0].rscarray = (resource_t **)xmalloc(sizeof(resource_t *)); + rcarray[0].rscarray[0] = rsc; + } + else + { + rccount++; + rcarray = (res_count_t *)xrealloc(rcarray, rccount * sizeof(res_count_t)); + rcarray[rccount-1].count = 1; + rcarray[rccount-1].type = nid; + rcarray[rccount-1].rscarray = (resource_t **)xmalloc(sizeof(resource_t *)); + rcarray[rccount-1].rscarray[0] = rsc; + } + } + else + { + rcp->count++; + rcp->rscarray = (resource_t **)xrealloc(rcp->rscarray, rcp->count * sizeof(resource_t *)); + rcp->rscarray[rcp->count-1] = rsc; + } + } + + if(!win32) + { + /* We're done, win16 requires no special sorting */ + return; + } + + /* We now have a unsorted list of types with an array of res_count_t + * in rcarray[0..rccount-1]. And we have names of one type in the + * rcarray[x].rsc[0..rcarray[x].count-1] arrays. + * The list needs to be sorted for win32's top level tree structure. + */ + + /* Sort the types */ + if(rccount > 1) + qsort(rcarray, rccount, sizeof(rcarray[0]), sort_type); + + /* Now sort the name-id arrays */ + for(i = 0; i < rccount; i++) + { + if(rcarray[i].count > 1) + qsort(rcarray[i].rscarray, rcarray[i].count, sizeof(rcarray[0].rscarray[0]), sort_name_id); + } + + /* Now split the name-id arrays into name/language + * subs. Don't look at the awfull expressions... + * We do this by taking the array elements out of rscarray and putting + * together a new array in rsc32array. + */ + for(i = 0; i < rccount; i++) + { + res_count_t *rcap; + + assert(rcarray[i].count >= 1); + + /* rcap points to the current type we are dealing with */ + rcap = &(rcarray[i]); + + /* Insert the first name-id */ + rcap->rsc32array = (res32_count_t *)xmalloc(sizeof(res32_count_t)); + rcap->count32 = 1; + rcap->rsc32array[0].rsc = (resource_t **)xmalloc(sizeof(resource_t *)); + rcap->rsc32array[0].count = 1; + rcap->rsc32array[0].rsc[0] = rcap->rscarray[0]; + if(rcap->rscarray[0]->name->type == name_ord) + { + rcap->n_id_entries = 1; + rcap->n_name_entries = 0; + } + else + { + rcap->n_id_entries = 0; + rcap->n_name_entries = 1; + } + + /* Now loop over the resting resources of the current type + * to find duplicate names (which should have different + * languages). + */ + for(j = 1; j < rcap->count; j++) + { + res32_count_t *r32cp; + + /* r32cp points to the current res32_count structure + * that holds the resource name we are processing. + */ + r32cp = &(rcap->rsc32array[rcap->count32-1]); + + if(!compare_name_id(r32cp->rsc[0]->name, rcarray[i].rscarray[j]->name)) + { + /* Names are the same, add to list */ + r32cp->count++; + r32cp->rsc = (resource_t **)xrealloc(r32cp->rsc, r32cp->count * sizeof(resource_t *)); + r32cp->rsc[r32cp->count-1] = rcap->rscarray[j]; + + if(rcap->rscarray[j]->name->type == name_ord) + { + rcap->n_id_entries = 1; + rcap->n_name_entries = 0; + } + else + { + rcap->n_id_entries = 0; + rcap->n_name_entries = 1; + } + } + else + { + /* New name-id, sort the old one by + * language and create new list + */ + if(r32cp->count > 1) + qsort(r32cp->rsc, r32cp->count, sizeof(r32cp->rsc[0]), sort_language); + rcap->count32++; + rcap->rsc32array = (res32_count_t*)xrealloc(rcap->rsc32array, rcap->count32 * sizeof(res32_count_t)); + rcap->rsc32array[rcap->count32-1].rsc = (resource_t **)xmalloc(sizeof(resource_t *)); + rcap->rsc32array[rcap->count32-1].count = 1; + rcap->rsc32array[rcap->count32-1].rsc[0] = rcap->rscarray[j]; + + if(rcap->rscarray[j]->name->type == name_ord) + rcap->n_id_entries++; + else + rcap->n_name_entries++; + } + } + /* Also sort the languages of the last name group */ + if(rcap->rsc32array[rcap->count32-1].count > 1) + qsort(rcap->rsc32array[rcap->count32-1].rsc, + rcap->rsc32array[rcap->count32-1].count, + sizeof(rcap->rsc32array[rcap->count32-1].rsc[0]), + sort_language); + } +} + +/* + ***************************************************************************** + * Function : write_pe_segment + * Syntax : void write_pe_segment(FILE *fp, resource_t *top) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void write_pe_segment(FILE *fp, resource_t *top) +{ + int i; + + fprintf(fp, "\t.align\t4\n"); + fprintf(fp, "%s%s:\n", prefix, _PEResTab); + fprintf(fp, "\t.globl\t%s%s\n", prefix, _PEResTab); + /* Flags */ + fprintf(fp, "\t.long\t0\n"); + /* Time/Date stamp */ + fprintf(fp, "\t.long\t0x%08lx\n", now); + /* Version */ + fprintf(fp, "\t.long\t0\n"); /* FIXME: must version be filled out? */ + /* # of id entries, # of name entries */ + fprintf(fp, "\t.short\t%d, %d\n", n_name_entries, n_id_entries); + + /* Write the type level of the tree */ + for(i = 0; i < rccount; i++) + { + res_count_t *rcp; + char *label; + + rcp = &rcarray[i]; + + /* TypeId */ + if(rcp->type.type == name_ord) + fprintf(fp, "\t.long\t%d\n", rcp->type.name.i_name); + else + { + char *name = prep_nid_for_label(&(rcp->type)); + fprintf(fp, "\t.long\t(%s_%s_typename - %s%s) | 0x80000000\n", + prefix, + name, + prefix, + _PEResTab); + } + /* Offset */ + label = prep_nid_for_label(&(rcp->type)); + fprintf(fp, "\t.long\t(.L%s - %s%s) | 0x80000000\n", + label, + prefix, + _PEResTab); + } + + /* Write the name level of the tree */ + + for(i = 0; i < rccount; i++) + { + res_count_t *rcp; + char *typelabel; + char *namelabel; + int j; + + rcp = &rcarray[i]; + + typelabel = xstrdup(prep_nid_for_label(&(rcp->type))); + fprintf(fp, ".L%s:\n", typelabel); + + fprintf(fp, "\t.long\t0\n"); /* Flags */ + fprintf(fp, "\t.long\t0x%08lx\n", now); /* TimeDate */ + fprintf(fp, "\t.long\t0\n"); /* FIXME: must version be filled out? */ + fprintf(fp, "\t.short\t%d, %d\n", rcp->n_name_entries, rcp->n_id_entries); + for(j = 0; j < rcp->count32; j++) + { + resource_t *rsc = rcp->rsc32array[j].rsc[0]; + /* NameId */ + if(rsc->name->type == name_ord) + fprintf(fp, "\t.long\t%d\n", rsc->name->name.i_name); + else + { + fprintf(fp, "\t.long\t(%s%s_name - %s%s) | 0x80000000\n", + prefix, + rsc->c_name, + prefix, + _PEResTab); + } + /* Maybe FIXME: Unescape the tree (ommit 0x80000000) and + * put the offset to the resource data entry. + * ?? Is unescaping worth while ?? + */ + /* Offset */ + namelabel = prep_nid_for_label(rsc->name); + fprintf(fp, "\t.long\t(.L%s_%s - %s%s) | 0x80000000\n", + typelabel, + namelabel, + prefix, + _PEResTab); + } + free(typelabel); + } + + /* Write the language level of the tree */ + + for(i = 0; i < rccount; i++) + { + res_count_t *rcp; + char *namelabel; + char *typelabel; + int j; + + rcp = &rcarray[i]; + typelabel = xstrdup(prep_nid_for_label(&(rcp->type))); + + for(j = 0; j < rcp->count32; j++) + { + res32_count_t *r32cp = &(rcp->rsc32array[j]); + int k; + + namelabel = xstrdup(prep_nid_for_label(r32cp->rsc[0]->name)); + fprintf(fp, ".L%s_%s:\n", typelabel, namelabel); + + fprintf(fp, "\t.long\t0\n"); /* Flags */ + fprintf(fp, "\t.long\t0x%08lx\n", now); /* TimeDate */ + fprintf(fp, "\t.long\t0\n"); /* FIXME: must version be filled out? */ + fprintf(fp, "\t.short\t0, %d\n", r32cp->count); + + for(k = 0; k < r32cp->count; k++) + { + resource_t *rsc = r32cp->rsc[k]; + assert(rsc->lan != NULL); + /* LanguageId */ + fprintf(fp, "\t.long\t0x%08x\n", rsc->lan ? MAKELANGID(rsc->lan->id, rsc->lan->sub) : 0); + /* Offset */ + fprintf(fp, "\t.long\t.L%s_%s_%d - %s%s\n", + typelabel, + namelabel, + rsc->lan ? MAKELANGID(rsc->lan->id, rsc->lan->sub) : 0, + prefix, + _PEResTab); + } + free(namelabel); + } + free(typelabel); + } + + /* Write the resource table itself */ + + for(i = 0; i < rccount; i++) + { + res_count_t *rcp; + char *namelabel; + char *typelabel; + int j; + + rcp = &rcarray[i]; + typelabel = xstrdup(prep_nid_for_label(&(rcp->type))); + + for(j = 0; j < rcp->count32; j++) + { + res32_count_t *r32cp = &(rcp->rsc32array[j]); + int k; + + namelabel = xstrdup(prep_nid_for_label(r32cp->rsc[0]->name)); + + for(k = 0; k < r32cp->count; k++) + { + resource_t *rsc = r32cp->rsc[k]; + + assert(rsc->lan != NULL); + + fprintf(fp, ".L%s_%s_%d:\n", + typelabel, + namelabel, + rsc->lan ? MAKELANGID(rsc->lan->id, rsc->lan->sub) : 0); + + /* Data RVA */ + fprintf(fp, "\t.long\t%s%s_data - %s%s\n", + prefix, + rsc->c_name, + prefix, + _PEResTab); + /* Size */ + fprintf(fp, "\t.long\t%d\n", + rsc->binres->size - rsc->binres->dataidx); + /* CodePage */ + fprintf(fp, "\t.long\t%ld\n", codepage); + /* Reserved */ + fprintf(fp, "\t.long\t0\n"); + } + free(namelabel); + } + free(typelabel); + } + +} + +/* + ***************************************************************************** + * Function : write_ne_segment + * Syntax : void write_ne_segment(FILE *fp, resource_t *top) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void write_ne_segment(FILE *fp, resource_t *top) +{ + int i, j; + + fprintf(fp, "\t.align\t4\n"); + fprintf(fp, "%s%s:\n", prefix, _NEResTab); + fprintf(fp, "\t.globl\t%s%s\n", prefix, _NEResTab); + + /* AlignmentShift */ + fprintf(fp, "\t.short\t%d\n", alignment_pwr); + + /* TypeInfo */ + for(i = 0; i < rccount; i++) + { + res_count_t *rcp = &rcarray[i]; + + /* TypeId */ + if(rcp->type.type == name_ord) + fprintf(fp, "\t.short\t0x%04x\n", rcp->type.name.i_name | 0x8000); + else + fprintf(fp, "\t.short\t%s_%s_typename - %s%s\n", + prefix, + rcp->type.name.s_name->str.cstr, + prefix, + _NEResTab); + /* ResourceCount */ + fprintf(fp, "\t.short\t%d\n", rcp->count); + /* Reserved */ + fprintf(fp, "\t.long\t0\n"); + /* NameInfo */ + for(j = 0; j < rcp->count; j++) + { + /* FIXME: dividing by `alignment` doesn't seem to + * work with as (GAS). Shifting results in the + * correct behaviour. Maybe an as bug or just my + * lack of knowing as expression-syntax. + */ + /* Offset */ +/* + * VERY IMPORTANT: + * The offset is relative to the beginning of the NE resource segment + * and _NOT_ to the file-beginning. This is because we do not have a + * file based resource, but a simulated NE segment. The offset _is_ + * scaled by the AlignShift field. + * All other things are as the MS doc describes (alignment etc.) + */ + fprintf(fp, "\t.short\t(%s%s_data - %s%s) >> %d\n", + prefix, + rcp->rscarray[j]->c_name, + prefix, + _NEResTab, + alignment_pwr); + /* Length */ + fprintf(fp, "\t.short\t%d\n", + rcp->rscarray[j]->binres->size - rcp->rscarray[j]->binres->dataidx); + /* Flags */ + fprintf(fp, "\t.short\t0x%04x\n", (WORD)rcp->rscarray[j]->memopt); + /* Id */ + if(rcp->rscarray[j]->name->type == name_ord) + fprintf(fp, "\t.short\t0x%04x\n", rcp->rscarray[j]->name->name.i_name | 0x8000); + else + fprintf(fp, "\t.short\t%s%s_name - %s%s\n", + prefix, + rcp->rscarray[j]->c_name, + prefix, + _NEResTab); + /* Handle and Usage */ + fprintf(fp, "\t.short\t0, 0\n"); + } + } + /* EndTypes */ + fprintf(fp, "\t.short\t0\n"); +} + +/* + ***************************************************************************** + * Function : write_rsc_names + * Syntax : void write_rsc_names(FILE *fp, resource_t *top) + * Input : + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void write_rsc_names(FILE *fp, resource_t *top) +{ + int i, j; + + if(win32) + { + /* Write the names */ + + for(i = 0; i < rccount; i++) + { + res_count_t *rcp; + + rcp = &rcarray[i]; + + if(rcp->type.type == name_str) + { + char *name = prep_nid_for_label(&(rcp->type)); + fprintf(fp, "%s_%s_typename:\n", + prefix, + name); + write_name_str(fp, &(rcp->type)); + } + + for(j = 0; j < rcp->count32; j++) + { + resource_t *rsc = rcp->rsc32array[j].rsc[0]; + + if(rsc->name->type == name_str) + { + fprintf(fp, "%s%s_name:\n", + prefix, + rsc->c_name); + write_name_str(fp, rsc->name); + } + } + } + } + else + { + /* ResourceNames */ + for(i = 0; i < rccount; i++) + { + res_count_t *rcp = &rcarray[i]; + + for(j = 0; j < rcp->count; j++) + { + if(rcp->type.type == name_str) + { + fprintf(fp, "%s_%s_typename:\n", + prefix, + rcp->type.name.s_name->str.cstr); + write_name_str(fp, &(rcp->type)); + } + if(rcp->rscarray[j]->name->type == name_str) + { + fprintf(fp, "%s%s_name:\n", + prefix, + rcp->rscarray[j]->c_name); + write_name_str(fp, rcp->rscarray[j]->name); + } + } + } + /* EndNames */ + + /* This is to end the NE resource table */ + if(create_dir) + fprintf(fp, "\t.byte\t0\n"); + } + + fprintf(fp, "\n"); +} + +/* + ***************************************************************************** + * Function : write_s_file + * Syntax : void write_s_file(char *outname, resource_t *top) + * Input : + * outname - Filename to write to + * top - The resource-tree to convert + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void write_s_file(char *outname, resource_t *top) +{ + FILE *fo; + resource_t *rsc; + + fo = fopen(outname, "wt"); + if(!fo) + { + error("Could not open %s\n", outname); + } + + now = time(NULL); + fprintf(fo, s_file_head_str, input_name, cmdline, ctime(&now)); + + /* Get an idea how many we have and restructure the tables */ + count_resources(top); + + /* First write the segment tables */ + if(create_dir) + { + if(win32) + write_pe_segment(fo, top); + else + write_ne_segment(fo, top); + } + + /* Dump the names */ + write_rsc_names(fo, top); + + if(!indirect_only) + { + /* Write the resource data */ + fprintf(fo, "#\n# Resource binary data\n#\n"); + for(rsc = top; rsc; rsc = rsc->next) + { + if(!rsc->binres) + continue; + + fprintf(fo, "\t.align\t%d\n", win32 ? 4 : alignment); + fprintf(fo, "%s%s_data:\n", prefix, rsc->c_name); + if(global) + fprintf(fo, "\t.globl\t%s%s_data\n", prefix, rsc->c_name); + + write_s_res(fo, rsc->binres); + + fprintf(fo, "\n"); + } + } + + if(indirect) + { + /* Write the indirection structures */ + fprintf(fo, "\n#\n# Resource indirection structures\n#\n"); + fprintf(fo, "\t.align\t4\n"); + for(rsc = top; rsc; rsc = rsc->next) + { + int type; + char *type_name = NULL; + + if(!rsc->binres) + continue; + + switch(rsc->type) + { + case res_menex: + type = WRC_RT_MENU; + break; + case res_dlgex: + type = WRC_RT_DIALOG; + break; + case res_usr: + assert(rsc->res.usr->type != NULL); + type_name = prep_nid_for_label(rsc->res.usr->type); + type = 0; + break; + default: + type = rsc->type; + } + + /* + * This follows a structure like: + * struct wrc_resource { + * INT32 id; + * RSCNAME *resname; + * INT32 restype; + * RSCNAME *typename; + * void *data; + * UINT32 datasize; + * }; + * The 'RSCNAME' is a pascal-style string where the + * first byte/word denotes the size and the rest the string + * itself. + */ + fprintf(fo, "%s%s:\n", prefix, rsc->c_name); + if(global) + fprintf(fo, "\t.globl\t%s%s\n", prefix, rsc->c_name); + fprintf(fo, "\t.long\t%d, %s%s%s, %d, %s%s%s%s, %s%s_data, %d\n", + rsc->name->type == name_ord ? rsc->name->name.i_name : 0, + rsc->name->type == name_ord ? "0" : prefix, + rsc->name->type == name_ord ? "" : rsc->c_name, + rsc->name->type == name_ord ? "" : "_name", + type, + type ? "0" : prefix, + type ? "" : "_", + type ? "" : type_name, + type ? "" : "_typename", + prefix, + rsc->c_name, + rsc->binres->size - rsc->binres->dataidx); + fprintf(fo, "\n"); + } + fprintf(fo, "\n"); + + /* Write the indirection table */ + fprintf(fo, "#\n# Resource indirection table\n#\n"); + fprintf(fo, "\t.align\t4\n"); + fprintf(fo, "%s%s:\n", prefix, _ResTable); + fprintf(fo, "\t.globl\t%s%s\n", prefix, _ResTable); + for(rsc = top; rsc; rsc = rsc->next) + { + fprintf(fo, "\t.long\t%s%s\n", prefix, rsc->c_name); + } + fprintf(fo, "\t.long\t0\n"); + fprintf(fo, "\n"); + } + + fprintf(fo, s_file_tail_str); + fclose(fo); +} + +/* + ***************************************************************************** + * Function : write_h_file + * Syntax : void write_h_file(char *outname, resource_t *top) + * Input : + * outname - Filename to write to + * top - The resource-tree to convert + * Output : + * Description : + * Remarks : + ***************************************************************************** +*/ +void write_h_file(char *outname, resource_t *top) +{ + FILE *fo; + resource_t *rsc; + char *h_prefix; + int cnameidx; + + if(prefix[0] == '_') + { + h_prefix = prefix + 1; + cnameidx = 0; + } + else if(prefix[0] == '\0') + { + h_prefix = prefix; + cnameidx = 1; + } + else + { + h_prefix = prefix; + cnameidx = 0; + warning("Resources might not be visible in c-namespace (missing '_' in prefix)\n"); + } + + fo = fopen(outname, "wt"); + if(!fo) + { + error("Could not open %s\n", outname); + } + + time(&now); + fprintf(fo, h_file_head_str, input_name, cmdline, ctime(&now), now, now); + + /* First write the segment tables reference */ + if(create_dir) + { + fprintf(fo, "extern %schar %s%s[];\n\n", + constant ? "const " : "", + h_prefix, + (win32 ? _PEResTab : _NEResTab) + cnameidx); + } + + /* Write the resource data */ + for(rsc = top; global && rsc; rsc = rsc->next) + { + if(!rsc->binres) + continue; + + fprintf(fo, "extern %schar %s%s_data[];\n", + constant ? "const " : "", + h_prefix, + rsc->c_name + cnameidx); + } + + if(indirect) + { + if(global) + fprintf(fo, "\n"); + + /* Write the indirection structures */ + for(rsc = top; global && rsc; rsc = rsc->next) + { + fprintf(fo, "extern %swrc_resource%d_t %s%s;\n", + constant ? "const " : "", + win32 ? 32 : 16, + h_prefix, + rsc->c_name + cnameidx); + } + + if(global) + fprintf(fo, "\n"); + + /* Write the indirection table */ + fprintf(fo, "extern %swrc_resource%d_t %s%s[];\n\n", + constant ? "const " : "", + win32 ? 32 : 16, + h_prefix, + _ResTable + cnameidx); + } + + fprintf(fo, h_file_tail_str); + fclose(fo); +} + +
diff --git a/tools/wrc/writeres.h b/tools/wrc/writeres.h new file mode 100644 index 0000000..08da1b2 --- /dev/null +++ b/tools/wrc/writeres.h
@@ -0,0 +1,19 @@ +/* + * Write resource prototypes + * + * Copyright 1998 Bertho A. Stultiens (BS) + * + */ + +#ifndef __WRC_WRITERES_H +#define __WRC_WRITERES_H + +#ifndef __WRC_WRCTYPES_H +#include "wrctypes.h" +#endif + +void write_resfile(char *outname, resource_t *top); +void write_s_file(char *outname, resource_t *top); +void write_h_file(char *outname, resource_t *top); + +#endif