Release 1.1

- added symbolic constants
- added simple expressions
- cleaned up docs


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5722 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
mahlzeit 2003-12-22 16:01:04 +00:00
parent 16b663b7ab
commit eed2e48e69
14 changed files with 597 additions and 602 deletions

View File

@ -3,7 +3,7 @@
#---------------------------------------------------------------------------
# General configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = "librdef API version 1"
PROJECT_NAME = "librdef API version 2"
PROJECT_NUMBER =
OUTPUT_DIRECTORY =
OUTPUT_LANGUAGE = English

View File

@ -85,6 +85,13 @@ struct id_t
char* name;
};
// Describes a symbolic constant.
struct define_t
{
char* name;
int32 value;
};
// The output file we add resources to.
extern BResources rsrc;
extern const char* rsrc_file;

View File

@ -6,12 +6,7 @@
<H1>Differences with beres/deres</H1>
<P>Some of your old rdef files may fail to compile with rc. Besides different
command line arguments, rc also has slightly different syntax and semantics.
These changes were made to allow for the future expansion of the rdef grammar
and to make resource scripts easier to write and understand. The compiler
should be backwards compatible with the majority of old scripts, but in some
cases it was necessary to break stuff.</P>
<P>Some of your old rdef files may fail to compile with rc. Besides different command line arguments, rc also has slightly different syntax and semantics. These changes were made to allow for the future expansion of the rdef grammar and to make resource scripts easier to write and understand. The compiler should be backwards compatible with the majority of old scripts, but in some cases it was necessary to break stuff.</P>
<H2>Differences with beres</H2>
@ -19,62 +14,27 @@ cases it was necessary to break stuff.</P>
<LI>We allow the \0 escape in string literals.</LI>
<LI>Our handling of the minus sign is more restrictive; we don't treat it like
a unary minus operator, which means you may only put one minus in front of a
number to negate it (i.e. "- -10" is an error).</LI>
<LI>Our handling of the minus sign is more restrictive; we don't treat it like a unary minus operator, which means you may only put one minus in front of a number to negate it (i.e. "- -10" is an error).</LI>
<LI>beres allows #define statements, but apparently ignores them. The compiler
reports no error, but any symbols you define are not recognized later on. We
don't allow #defines (yet).</LI>
<LI>beres allows #define statements, but apparently ignores them. The compiler reports no error, but any symbols you define are not recognized later on. We don't allow #defines (yet).</LI>
<LI>beres allows you to put enum symbols in resource data, and replaces the
symbol with its value. For some reason, it lets you assign any kind of data to
enum symbols, such as strings and floats. Since that is not valid C/C++ syntax,
librdef does not allow this. As a result, it does not make much sense for us
to support enum symbols in resource data.</LI>
<LI>beres allows you to put enum symbols in resource data, and replaces the symbol with its value. For some reason, it lets you assign any kind of data to enum symbols, such as strings and floats. Since that is not valid C/C++ syntax, librdef does not allow this. As a result, it does not make much sense for us to support enum symbols in resource data.</LI>
<LI>We only allow a few type casts, but enough to be backwards compatible.
beres allows you to cast from bool to all other types, between strings and
numeric types, etc. The usefulness of this is limited, so to keep things simple
librdef doesn't allow that.</LI>
<LI>We only allow a few type casts, but enough to be backwards compatible. beres allows you to cast from bool to all other types, between strings and numeric types, etc. The usefulness of this is limited, so to keep things simple librdef doesn't allow that.</LI>
<LI>There is no need to put parentheses around a type code, even for simple
data; beres sometimes requires the parens and sometimes it doesn't. With rc,
they are always optional (preferred notation is without the parens).</LI>
<LI>There is no need to put parentheses around a type code, even for simple data; beres sometimes requires the parens and sometimes it doesn't. With rc, they are always optional (preferred notation is without the parens).</LI>
<LI>beres allows multiple type codes and/or type casts per resource
statement/data field, but we allow only one of each. Because we don't associate
type codes with data (but only with the resource itself or with individual
message fields), we don't allow the shortcut notation that lets you put a type
code and cast inside one set of parentheses.</LI>
<LI>beres allows multiple type codes and/or type casts per resource statement/data field, but we allow only one of each. Because we don't associate type codes with data (but only with the resource itself or with individual message fields), we don't allow the shortcut notation that lets you put a type code and cast inside one set of parentheses.</LI>
<LI>We do not allow arrays to have named fields, nor do we allow messages (and
archives) to have unnamed fields. beres apparently treats all compound data the
same, but we don't, because not all compound data is the same.</LI>
<LI>We do not allow arrays to have named fields, nor do we allow messages (and archives) to have unnamed fields. beres apparently treats all compound data the same, but we don't, because not all compound data is the same.</LI>
<LI>When specifying array data, the "array" keyword is required. beres allows
you to omit the array keyword and just put the data between braces, but that
conflicts with our handling of user-defined types.</LI>
<LI>When specifying array data, the "array" keyword is required. beres allows you to omit the array keyword and just put the data between braces, but that conflicts with our handling of user-defined types.</LI>
<LI>Field names in message resources may only be string literals. beres allows
identifiers but it converts them to string literals anyway. Just like normal
data, you may cast the field's data to a different type, but not to a different
type code. Specifying a non-standard type code is fine, but it goes in front of
the type name. Just like beres, rc considers "array", "message", and "archive"
to be valid data type names, but "message" and "archive" cannot be followed by
a "what" code (beres ignores that anyway). When you cast an archive to a
message, we don't strip the "class" field (in fact, we don't perform any
conversion).</LI>
<LI>Field names in message resources may only be string literals. beres allows identifiers but it converts them to string literals anyway. Just like normal data, you may cast the field's data to a different type, but not to a different type code. Specifying a non-standard type code is fine, but it goes in front of the type name. Just like beres, rc considers "array", "message", and "archive" to be valid data type names, but "message" and "archive" cannot be followed by a "what" code (beres ignores that anyway). When you cast an archive to a message, we don't strip the "class" field (in fact, we don't perform any conversion).</LI>
<LI>We allow users to define their own types. The built-in types from beres
(point, rect, and rgb_color) are re-implemented as such user-defined types. The
syntax for these three built-ins should still be backwards compatible with old
scripts.</LI>
<LI>We allow users to define their own types. The built-in types from beres (point, rect, and rgb_color) are re-implemented as such user-defined types. The syntax for these three built-ins should still be backwards compatible with old scripts.</LI>
<LI>beres automatically adds names for resources that are specified as
"resource(R_Symbol)", unless the "explicit names only" option is set. We do
that the other way around: we don't automatically add names unless the user
sets the "auto names" option.</LI>
<LI>beres automatically adds names for resources that are specified as "resource(R_Symbol)", unless the "explicit names only" option is set. We do that the other way around: we don't automatically add names unless the user sets the "auto names" option.</LI>
</UL>
@ -86,8 +46,7 @@ sets the "auto names" option.</LI>
<LI>We put opening braces on the next line (heh heh).</LI>
<LI>Type codes that cannot be represented as #'xxxx' are written as #num where
num is a decimal number, not hexadecimal.</LI>
<LI>Type codes that cannot be represented as #'xxxx' are written as #num where num is a decimal number, not hexadecimal.</LI>
<LI>Floats and doubles have a bunch of extra zeroes.</LI>

View File

@ -7,87 +7,47 @@
<H1>rdef grammar</H1>
<P>This is the (somewhat boring) specification of the rdef file format as it is
understood by librdef. It also describes to a certain extent how the compiler
works. You don't need to read this unless you want to hack librdef. Knowledge
of compiler theory and lex/yacc is assumed.</P>
<P>Version 1.1</P>
<P>This is the (somewhat boring) specification of the rdef file format as it is understood by librdef. It also describes to a certain extent how the compiler works. You don't need to read this unless you want to hack librdef. Knowledge of compiler theory and lex/yacc is assumed.</P>
<H2>The lexer</H2>
<P>Like any compiler, librdef contains a lexer (aka scanner) and a parser. The
lexer reads the input file and chops it up into tokens. The lexer ignores
single-line // comments and /* ... */ multi-line comments. It also ignores
whitespace and newlines.</P>
<P>Like any compiler, librdef contains a lexer (aka scanner) and a parser. The lexer reads the input file and chops it up into tokens. The lexer ignores single-line // comments and /* ... */ multi-line comments. It also ignores whitespace and newlines.</P>
<P>The lexer recognizes the following tokens:</P>
<BLOCKQUOTE>
<TABLE BORDER="1">
<BLOCKQUOTE><TABLE BORDER=1 SUMMARY="">
<TR><TD WIDTH="20%">BOOL</TD><TD>true or false</TD></TR>
<TR><TD>INTEGER</TD><TD>You can specify integers as decimal numbers,
hexadecimal numbers (starting with 0x or 0X, alpha digits are case
insensitive), octal numbers (starting with a leading 0), binary numbers
(starting with 0b or 0B), or as a four character code ('CCCC'). Valid range is
64 bits. At this point, numbers are always unsigned. The minus sign is treated
as a separate token, and is dealt with by the parser.</TD></TR>
<TR><TD>INTEGER</TD><TD>You can specify integers as decimal numbers, hexadecimal numbers (starting with 0x or 0X, alpha digits are case insensitive), octal numbers (starting with a leading 0), binary numbers (starting with 0b or 0B), or as a four character code ('CCCC'). Valid range is 64 bits. At this point, numbers are always unsigned. The minus sign is treated as a separate token, and is dealt with by the parser.</TD></TR>
<TR><TD>FLOAT</TD><TD>A floating point literal. Must contain a decimal point,
may contain a signed exponent. Stored internally as a double.</TD></TR>
<TR><TD>FLOAT</TD><TD>A floating point literal. Must contain a decimal point, may contain a signed exponent. Stored internally as a double.</TD></TR>
<TR><TD>STRING</TD><TD>UTF-8 compatible string literal, enclosed by double
quotes. Can contain escape sequences (\b \f \n \r \t \v \" \\ \0), octal
escapes (\000) and hex escapes (\0x00 or \x00). May not span more than one
line, although you are allowed to specify multiple string literals in a row and
the lexer will automatically concatenate them. There is no maximum length.</TR>
<TR><TD>STRING</TD><TD>UTF-8 compatible string literal, enclosed by double quotes. Can contain escape sequences (\b \f \n \r \t \v \" \\ \0), octal escapes (\000) and hex escapes (\0x00 or \x00). May not span more than one line, although you are allowed to specify multiple string literals in a row and the lexer will automatically concatenate them. There is no maximum length.</TR>
<TR><TD>RAW</TD><TD>Hexadecimal representation of raw data, enclosed by double
quotes, and prefixed by a dollar sign: $"12FFAB". Each byte is represented by
two hex characters, so there must be an even number of characters between the
quotes. The alpha digits are not case sensitive. Like STRING, a RAW token may
not span more than one line, but multiple consecutive RAW tokens are
automatically concatenated. No maximum length.</TD></TR>
<TR><TD>RAW</TD><TD>Hexadecimal representation of raw data, enclosed by double quotes, and prefixed by a dollar sign: $"12FFAB". Each byte is represented by two hex characters, so there must be an even number of characters between the quotes. The alpha digits are not case sensitive. Like STRING, a RAW token may not span more than one line, but multiple consecutive RAW tokens are automatically concatenated. No maximum length.</TD></TR>
<TR><TD>IDENT</TD><TD>C/C++ identifier. First character is alphabetic or
underscore. Other characters are alphanumeric or underscore.</TD></TR>
<TR><TD>IDENT</TD><TD>C/C++ identifier. First character is alphabetic or underscore. Other characters are alphanumeric or underscore.</TD></TR>
<TR><TD>TYPECODE</TD><TD>A hash sign followed by a 32-bit unsigned decimal
number, hex number, or four character code. Examples: #200, #0x00C8,
#'MIMS'</TD></TR>
<TR><TD>TYPECODE</TD><TD>A hash sign followed by a 32-bit unsigned decimal number, hex number, or four character code. Examples: #200, #0x00C8, #'MIMS'</TD></TR>
</TABLE>
</BLOCKQUOTE>
</TABLE></BLOCKQUOTE>
<P>The following are treated as keywords and special symbols:</P>
<BLOCKQUOTE><CODE>enum resource array message archive type import
{ } ( ) , ; = -</CODE></BLOCKQUOTE>
<BLOCKQUOTE><CODE>enum resource array message archive type import { } ( ) , ; = - + * / % ^ | &amp; ~</CODE></BLOCKQUOTE>
<P>The lexer also deals with #include statements, which look like: #include
"filename"\n. When you #include a file, the lexer expects it to contain valid
rdef syntax. So even though the include file is probably a C/C++ header, it
should not contain anything but the enum statement and/or comments. The lexer
only looks for include files in the include search paths that you have
specified, so if you want it to look in the current working directory you have
to explicitly specify that. You may nest #includes.</P>
<P>The lexer also deals with #include statements, which look like: #include "filename"\n. When you #include a file, the lexer expects it to contain valid rdef syntax. So even though the include file is probably a C/C++ header, it should not contain anything but the enum statement and/or comments. The lexer only looks for include files in the include search paths that you have specified, so if you want it to look in the current working directory you have to explicitly specify that. You may nest #includes.</P>
<P>A note about UTF-8. Since the double quote (hex 0x22) is never part of the
second or third byte of a UTF-8 character, the lexer can safely deal with UTF-8
characters inside string literals. That is also the reason that the decompiler
does not escape characters that are not human-readable (except the ones in the
7-bit ASCII range), because they could be part of a UTF-8 encoding. The current
version of librdef does not handle L"..." (wide char) strings, but nobody uses
them anyway.</P>
<P>A note about UTF-8. Since the double quote (hex 0x22) is never part of the second or third byte of a UTF-8 character, the lexer can safely deal with UTF-8 characters inside string literals. That is also the reason that the decompiler does not escape characters that are not human-readable (except the ones in the 7-bit ASCII range), because they could be part of a UTF-8 encoding. The current version of librdef does not handle L"..." (wide char) strings, but nobody uses them anyway.</P>
<H2>The parser</H2>
<P>The parser takes the tokens from the lexer and matches them against the
rules of the grammar. What follows is the grammar in a simplified variation of
BNF, so the actual bison source file may look a little different. Legend:</P>
<P>The parser takes the tokens from the lexer and matches them against the rules of the grammar. What follows is the grammar in a simplified variation of BNF, so the actual bison source file may look a little different. Legend:</P>
<BLOCKQUOTE>
<TABLE BORDER="1">
<BLOCKQUOTE><TABLE BORDER=1 SUMMARY="">
<TR><TD>[ a ]</TD><TD>match a 0 or 1 times</TD></TR>
<TR><TD>{ b }</TD><TD>match b 0 or more times</TD></TR>
<TR><TD>c | d</TD><TD>match either c or d</TD></TR>
@ -95,34 +55,31 @@ BNF, so the actual bison source file may look a little different. Legend:</P>
<TR><TD>lowercase</TD><TD>nonterminal</TD></TR>
<TR><TD>UPPER</TD><TD>token from the lexer</TD></TR>
<TR><TD>'c'</TD><TD>token from the lexer</TD></TR>
</TABLE>
</BLOCKQUOTE>
</TABLE></BLOCKQUOTE>
<P>The rdef grammar consists of the following rules:</P>
<BLOCKQUOTE>
<TABLE BORDER="1">
<BLOCKQUOTE><TABLE BORDER=1 SUMMARY="">
<TR><TD>script</TD><TD>{enum | typedef | resource}</TD></TR>
<TR><TD>enum</TD><TD> ENUM '{' [symboldef {',' symboldef} [',']] '}'
';'</TD></TR>
<TR><TD>enum</TD><TD> ENUM '{' [symboldef {',' symboldef} [',']] '}' ';'</TD></TR>
<TR><TD>symboldef</TD><TD>IDENT ['=' integer]</TD></TR>
<TR><TD>typedef</TD><TD>TYPE [id] [TYPECODE] IDENT '{' fielddef {',' fieldef}
'}' ';'</TD></TR>
<TR><TD>typedef</TD><TD>TYPE [id] [TYPECODE] IDENT '{' fielddef {',' fielddef} '}' ';'</TD></TR>
<TR><TD>fielddef</TD><TD>datatype IDENT ['[' INTEGER ']'] ['=' data]</TD></TR>
<TR><TD>fielddef</TD><TD>datatype IDENT ['[' INTEGER ']'] ['=' expr]</TD></TR>
<TR><TD>resource</TD><TD>RESOURCE [id] [typecode] data ';'</TD></TR>
<TR><TD>resource</TD><TD>RESOURCE [id] [typecode] expr ';'</TD></TR>
<TR><TD>id</TD><TD>'(' [(integer | IDENT) [',' STRING] | STRING] ')'</TD></TR>
<TR><TD>typecode</TD><TD>['('] TYPECODE [')']</TD></TR>
<TR><TD>data</TD><TD>[typecast] (BOOL | integer | float | STRING | RAW | array
| message | archive | type)</TD></TR>
<TR><TD>expr</TD><TD>expr BINARY_OPER expr | UNARY_OPER expr | data</TD></TR>
<TR><TD>data</TD><TD>[typecast] (BOOL | integer | float | STRING | RAW | array | message | archive | type | define | '(' expr ')' )</TD></TR>
<TR><TD>typecast</TD><TD>['(' datatype ')']</TD></TR>
@ -132,79 +89,57 @@ BNF, so the actual bison source file may look a little different. Legend:</P>
<TR><TD>float</TD><TD>['-'] FLOAT</TD></TR>
<TR><TD>array</TD><TD>ARRAY ['{' [data {',' data}] '}'] | [ARRAY] IMPORT
STRING</TD></TR>
<TR><TD>array</TD><TD>ARRAY ['{' [expr {',' expr}] '}'] | [ARRAY] IMPORT STRING</TD></TR>
<TR><TD>message</TD><TD>MESSAGE ['(' integer ')'] ['{' [msgfield {','
msgfield}] '}']</TD></TR>
<TR><TD>message</TD><TD>MESSAGE ['(' integer ')'] ['{' [msgfield {',' msgfield}] '}']</TD></TR>
<TR><TD>msgfield</TD><TD>[TYPECODE] [datatype] STRING '=' data</TD></TR>
<TR><TD>msgfield</TD><TD>[TYPECODE] [datatype] STRING '=' expr</TD></TR>
<TR><TD>archive</TD><TD>ARCHIVE [archiveid] IDENT '{' msgfield {',' msgfield}
'}'</TD></TR>
<TR><TD>archive</TD><TD>ARCHIVE [archiveid] IDENT '{' msgfield {',' msgfield} '}'</TD></TR>
<TR><TD>archiveid</TD><TD>'(' [STRING] [',' integer] ')'</TD></TR>
<TR><TD>type</TD><TD>IDENT [data | '{' [typefield {',' typefield}]
'}']</TD></TR>
<TR><TD>type</TD><TD>IDENT [data | '{' [typefield {',' typefield}] '}']</TD></TR>
<TR><TD>typefield</TD><TD>[IDENT '='] data</TD></TR>
<TR><TD>typefield</TD><TD>[IDENT '='] expr</TD></TR>
</TABLE>
</BLOCKQUOTE>
<TR><TD>define</TD><TD>IDENT</TD></TR>
</TABLE></BLOCKQUOTE>
<H2>Semantics</H2>
<H3>Resource names</H3>
<P>There are several different ways to specify the ID and name of a new
resource:</P>
<P>There are several different ways to specify the ID and name of a new resource:</P>
<BLOCKQUOTE>
<TABLE BORDER="1">
<BLOCKQUOTE><TABLE BORDER=1 SUMMARY="">
<TR><TD WIDTH="25%">resource</TD><TD>The resource is assigned the default name
and ID of its data type.</TD></TR>
<TR><TD WIDTH="25%">resource</TD><TD>The resource is assigned the default name and ID of its data type.</TD></TR>
<TR><TD>resource()</TD><TD>The resource is assigned the default name and ID of
its data type.</TD></TR>
<TR><TD>resource()</TD><TD>The resource is assigned the default name and ID of its data type.</TD></TR>
<TR><TD>resource(1)</TD><TD>The resource is assigned the numeric ID 1, and the
default name of its data type.</TD></TR>
<TR><TD>resource(1)</TD><TD>The resource is assigned the numeric ID 1, and the default name of its data type.</TD></TR>
<TR><TD>resource("xxx")</TD><TD>The resource is assigned the name "xxx" and the
default ID of its data type.</TD></TR>
<TR><TD>resource("xxx")</TD><TD>The resource is assigned the name "xxx" and the default ID of its data type.</TD></TR>
<TR><TD>resource(1, "xxx")</TD><TD>The resource is assigned the numeric ID 1,
and the name "xxx".</TD></TR>
<TR><TD>resource(1, "xxx")</TD><TD>The resource is assigned the numeric ID 1, and the name "xxx".</TD></TR>
<TR><TD>resource(sss)</TD><TD>The resource is assigned the numeric ID that
corresponds with the symbol sss, which should have been defined in an enum
earlier. If the "auto names" option is passed to the compiler, the resource is
also given the name "sss", otherwise the default name from its data type is
used.</TD></TR>
<TR><TD>resource(sss)</TD><TD>The resource is assigned the numeric ID that corresponds with the symbol sss, which should have been defined in an enum earlier. If the "auto names" option is passed to the compiler, the resource is also given the name "sss", otherwise the default name from its data type is used.</TD></TR>
<TR><TD>resource(sss, "xxx")</TD><TD>The resource is assigned the numeric ID
that corresponds with the symbol sss, and the name "xxx".</TD></TR>
<TR><TD>resource(sss, "xxx")</TD><TD>The resource is assigned the numeric ID that corresponds with the symbol sss, and the name "xxx".</TD></TR>
</TABLE>
</BLOCKQUOTE>
</TABLE></BLOCKQUOTE>
<H3>Data types and type casts</H3>
<P>Resources (and message fields) have a type code and a data type. The data
type determines the format the data is stored in, while the type code tells the
user how to interpret the data. Typically, there is some kind of relation
between the two, otherwise the resource will be a little hard to read.</P>
<P>Resources (and message fields) have a type code and a data type. The data type determines the format the data is stored in, while the type code tells the user how to interpret the data. Typically, there is some kind of relation between the two, otherwise the resource will be a little hard to read.</P>
<P>The following table lists the compiler's built-in data types. (Users can
also define their own types; this is described in a later section.)</P>
<P>The following table lists the compiler's built-in data types. (Users can also define their own types; this is described in a later section.)</P>
<BLOCKQUOTE>
<BLOCKQUOTE><TABLE BORDER=1 SUMMARY=""><TR><TD VALIGN="top">
<TABLE BORDER="0"><TR><TD VALIGN="top">
<TABLE BORDER="1">
<TABLE BORDER=1 SUMMARY="">
<TR><TD>bool</TD><TD>B_BOOL_TYPE</TD></TR>
<TR><TD>int8</TD><TD>B_INT8_TYPE</TD></TR>
<TR><TD>uint8</TD><TD>B_UINT8_TYPE</TD></TR>
@ -220,7 +155,7 @@ also define their own types; this is described in a later section.)</P>
</TD><TD VALIGN="top">
<TABLE BORDER="1">
<TABLE BORDER=1 SUMMARY="">
<TR><TD>off_t</TD><TD>B_OFF_T_TYPE</TD></TR>
<TR><TD>time_t</TD><TD>B_TIME_TYPE</TD></TR>
<TR><TD>float</TD><TD>B_FLOAT_TYPE</TD></TR>
@ -233,70 +168,50 @@ also define their own types; this is described in a later section.)</P>
<TR><TD>archive</TD><TD>B_MESSAGE_TYPE</TD></TR>
</TABLE>
</TD></TR></TABLE>
</BLOCKQUOTE>
</TD></TR></TABLE></BLOCKQUOTE>
<P>The type code has no effect on how the data is stored. For example, if you
do this: "resource(x) #'LONG' true", then the data will not automatically be
stored as a 32-bit number! If you don't specify an explicit type code, the
compiler uses the type of the data for that.</P>
<P>The type code has no effect on how the data is stored. For example, if you do this: "resource(x) #'LONG' true", then the data will not automatically be stored as a 32-bit number! If you don't specify an explicit type code, the compiler uses the type of the data for that.</P>
<P>You can change the data type with a type cast. The following casts are
allowed:</P>
<P>You can change the data type with a type cast. The following casts are allowed:</P>
<BLOCKQUOTE>
<TABLE BORDER="1">
<BLOCKQUOTE><TABLE BORDER=1 SUMMARY="">
<TR><TD WIDTH="20%">bool</TD><TD>You cannot cast bool data.</TD></TR>
<TR><TD>integer</TD><TD>You can cast to all numeric data types. Casts to
smaller datatypes will truncate the number. Casting negative numbers to
unsigned datatypes (and vice versa) will wrap them, i.e. (uint8) -1 becomes
255.</TD></TR>
<TR><TD>integer</TD><TD>You can cast to all numeric data types. Casts to smaller datatypes will truncate the number. Casting negative numbers to unsigned datatypes (and vice versa) will wrap them, i.e. (uint8) -1 becomes 255.</TD></TR>
<TR><TD>floating point</TD><TD>You can only cast to float or double.</TD></TR>
<TR><TD>string</TD><TD>You cannot cast string data.</TD></TR>
<TR><TD>raw, buffer, array</TD><TD>You can cast anything to raw, but not the
other way around.</TD></TR>
<TR><TD>raw, buffer, array</TD><TD>You can cast anything to raw, but not the other way around.</TD></TR>
<TR><TD>message, archive</TD><TD>You cannot cast message data.</TD></TR>
<TR><TD>type</TD><TD>You cannot cast user-defined types.</TD></TR>
</TABLE>
</BLOCKQUOTE>
</TABLE></BLOCKQUOTE>
<P>In addition to the "simple" built-in data types, the compiler also natively
supports several data structures from the BeOS API (point, rect, rgb_color) and
a few convenience types (app_signature, app_flags, etc). These types all follow
the same rules as user-defined types.</P>
<P>In addition to the "simple" built-in data types, the compiler also natively supports several data structures from the BeOS API (point, rect, rgb_color) and a few convenience types (app_signature, app_flags, etc). These types all follow the same rules as user-defined types.</P>
<H3>Arrays</H3>
<P>The following definitions are semantically
equivalent:</P>
<P>The following definitions are semantically equivalent:</P>
<BLOCKQUOTE><PRE>resource(x) $"AABB";
resource(x) array { $"AA" $"BB" };
resource(x) array { $"AA", $"BB" };</PRE></BLOCKQUOTE>
<P>The comma is optional and simply concatenates the two literals. When you
decompile this code, it always looks like:</P>
<P>The comma is optional and simply concatenates the two literals. When you decompile this code, it always looks like:</P>
<BLOCKQUOTE><PRE>resource(x) $"AABB";</PRE></BLOCKQUOTE>
<P>Strings behave differently. The following two definitions are equivalent,
and concatenate the two literals into one string:</P>
<P>Strings behave differently. The following two definitions are equivalent, and concatenate the two literals into one string:</P>
<BLOCKQUOTE><PRE>resource(x) "AA" "BB";
resource(x) #'CSTR' array { "AA" "BB" };</PRE></BLOCKQUOTE>
<P>However, if you put a comma between the the strings, the compiler will still
glue them together but with a '\0' character in the middle. Now the resource
contains <I>two</I> strings: "AA" and "BB". You can also specify the '\0'
character yourself:</P>
<P>However, if you put a comma between the the strings, the compiler will still glue them together but with a '\0' character in the middle. Now the resource contains <I>two</I> strings: "AA" and "BB". You can also specify the '\0' character yourself:</P>
<BLOCKQUOTE><PRE>resource(x) "AA\0BB";
resource(x) #'CSTR' array { "AA", "BB" };</PRE></BLOCKQUOTE>
@ -306,133 +221,85 @@ resource(x) #'CSTR' array { "AA", "BB" };</PRE></BLOCKQUOTE>
<BLOCKQUOTE><PRE>resource(x) "AA", "BB";
resource(x) $"AA", $"BB";</PRE></BLOCKQUOTE>
<P>Note that the data type of an array is always raw data, no matter how you
specify its contents. Because raw literals may be empty ($""), so may
arrays.</P>
<P>Note that the data type of an array is always raw data, no matter how you specify its contents. Because raw literals may be empty ($""), so may arrays.</P>
<H3>Messages and archives</H3>
<P>A message resource is a flattened BMessage. By default it has the data type
B_MESSAGE_TYPE and corresponding type code #'MSGG'. If you don't specify a
"what" code for the message, it defaults to 0.</P>
<P>A message resource is a flattened BMessage. By default it has the data type B_MESSAGE_TYPE and corresponding type code #'MSGG'. If you don't specify a "what" code for the message, it defaults to 0.</P>
<P>Message fields assume the type of their data, unless you specify a different
type in front of the field name. (Normal casting rules apply here.) You can
also give the field a different type code, which tells the BMessage how to
interpret the data, but not how it is stored in the message. This type code
also goes in front of the field name. You can give the same name to multiple
fields, provided that they all have the same type. (The data of these fields
does not have to be the same size.) A message may be empty; it is still a valid
BMessage, but it contains no fields.</P>
<P>Message fields assume the type of their data, unless you specify a different type in front of the field name. (Normal casting rules apply here.) You can also give the field a different type code, which tells the BMessage how to interpret the data, but not how it is stored in the message. This type code also goes in front of the field name. You can give the same name to multiple fields, provided that they all have the same type. (The data of these fields does not have to be the same size.) A message may be empty; it is still a valid BMessage, but it contains no fields.</P>
<P>An archive is also a flattened BMessage, but one that was made by
Archive()'ing a BArchivable class, such as BBitmap. The name of the archive, in
this case BBitmap, is automatically added to the message in a field called
"class". The "archive" keyword is optionally followed by a set of parentheses
that enclose a string and/or an integer. The int is the "what" code, the string
is stored in a field called "add_on" (used for dynamic loading of
BArchivables). Other than that, archives and messages are identical. The
compiler does not check whether the contents of the archive actually make
sense, so if you don't structure the data properly you may be unable to
unarchive the object later. Unlike a message, an archive may not be empty,
because that is pointless.</P>
<P>An archive is also a flattened BMessage, but one that was made by Archive()'ing a BArchivable class, such as BBitmap. The name of the archive, in this case BBitmap, is automatically added to the message in a field called "class". The "archive" keyword is optionally followed by a set of parentheses that enclose a string and/or an integer. The int is the "what" code, the string is stored in a field called "add_on" (used for dynamic loading of BArchivables). Other than that, archives and messages are identical. The compiler does not check whether the contents of the archive actually make sense, so if you don't structure the data properly you may be unable to unarchive the object later. Unlike a message, an archive may not be empty, because that is pointless.</P>
<H3>User-defined types</H3>
<P>We allow users to define their own types. A "type" is just a fancy array,
because the data from the various fields is simply concatenated into one big
block of bytes. The difference is that user-defined types are much easier to
fill in.</P>
<P>We allow users to define their own types. A "type" is just a fancy array, because the data from the various fields is simply concatenated into one big block of bytes. The difference is that user-defined types are much easier to fill in.</P>
<P>A user-defined type has a symbolic name, a type code, and a number of data
fields. After all the fields have been concatenated, the type code is applied
to the whole block. So, the data type of this resource is always the same as
its type code (unlike arrays, which are always raw data). If no type code is
specified, it defaults to B_RAW_TYPE.</P>
<P>A user-defined type has a symbolic name, a type code, and a number of data fields. After all the fields have been concatenated, the type code is applied to the whole block. So, the data type of this resource is always the same as its type code (unlike arrays, which are always raw data). If no type code is specified, it defaults to B_RAW_TYPE.</P>
<P>The data fields always have a default value. For simple fields this is
typically 0 (numeric types) or empty (string, raw, message). The default value
of a user-defined type as a whole is the combination of the default values of
its fields. Of course, the user can specify other defaults. (When a user
creates a new resource that uses such a type, he is basically overriding the
default values with his own.)</P>
<P>The data fields always have a default value. For simple fields this is typically 0 (numeric types) or empty (string, raw, message). The default value of a user-defined type as a whole is the combination of the default values of its fields. Of course, the user can specify other defaults. (When a user creates a new resource that uses such a type, he is basically overriding the default values with his own.)</P>
<P>The user may fill in the data fields by name, by order, or using a
combination of both. Every time the compiler sees an unnamed data item, it
stuffs it into the next available field. Named data items are simply assigned
to the field with the same name, and may overwrite a value that was previously
put there "by order". Any fields that are not filled in keep their default
value. For example:</P>
<P>The user may fill in the data fields by name, by order, or using a combination of both. Every time the compiler sees an unnamed data item, it stuffs it into the next available field. Named data items are simply assigned to the field with the same name, and may overwrite a value that was previously put there "by order". Any fields that are not filled in keep their default value. For example:</P>
<BLOCKQUOTE><PRE>type vector { int32 x, int32 y, int32 z, int32 w = 4 };
resource(1) vector { 1, 3, x = 2 };</PRE></BLOCKQUOTE>
<P>Here, x is first set to 1, y is set to 3, x is now overwritten by the value
2, z is given the default value 0, and w defaults to 4.</P>
<P>Here, x is first set to 1, y is set to 3, x is now overwritten by the value 2, z is given the default value 0, and w defaults to 4.</P>
<P>Note: if a user-defined type contains string, raw, or message fields, the
size of the type depends on the data that the user puts into it, because these
fields have a variable size. However, the user may specify a fixed size for a
field (number of bytes, enclosed in square brackets following the field name).
In this case, data that is too long will be truncated and data that is too
short will be padded with zeroes. You can do this for all types, but it really
only makes sense for strings and raw data.</P>
<P>Note: if a user-defined type contains string, raw, or message fields, the size of the type depends on the data that the user puts into it, because these fields have a variable size. However, the user may specify a fixed size for a field (number of bytes, enclosed in square brackets following the field name). In this case, data that is too long will be truncated and data that is too short will be padded with zeroes. You can do this for all types, but it really only makes sense for strings and raw data. More about this in the manual.</P>
<P>A type definition may also contain a default resource ID and name. The
default ID of built-in types is usually 1 and the name is empty (NULL). For
example:</P>
<P>A type definition may also contain a default resource ID and name. The default ID of built-in types is usually 1 and the name is empty (NULL). For example:</P>
<BLOCKQUOTE><PRE>type(10, "MyName") mytype { int32 a };
resource mytype 123;</PRE></BLOCKQUOTE>
<P>The resource is now called "MyName" and has ID 10. Obviously you can only do
this once or you will receive a duplicate resource error. If this type is used
inside an array or other compound type, the default ID and resource name are
ignored. Note: this feature introduces a shift/reduce conflict in the
compiler:</P>
<P>The resource is now called "MyName" and has ID 10. Obviously you can only do this once or you will receive a duplicate resource error. If this type is used inside an array or other compound type, the default ID and resource name are ignored. Note: this feature introduces a shift/reduce conflict in the compiler:</P>
<BLOCKQUOTE><PRE>resource (int8) 123;</PRE></BLOCKQUOTE>
<P>This probably doesn't do what you expect. The compiler now considers the
"(int8)" to be the resource ID, not a typecast. If you did not declare "int8"
in an enum (probably not), this gives a compiler error. Not a big problem,
because it is unlikely that you will ever do this. Here is a workaround:</P>
<P>This probably doesn't do what you expect. The compiler now considers the "(int8)" to be the resource ID, not a typecast. If you did not declare "int8" in an enum (probably not), this gives a compiler error. Not a big problem, because it is unlikely that you will ever do this. Here is a workaround:</P>
<BLOCKQUOTE><PRE>resource() (int8) 123;</PRE></BLOCKQUOTE>
<H3>The grammar and Bison</H3>
<P>Above I mentioned one of the shift/reduce conflicts from the grammar. There are several others. These are mostly the result of keeping the original grammar intact as much as possible, without having to introduce weird syntax rules for the new features. These issues aren't fatal but if you try to do something funky in your script, you may get an error message.</P>
<P>The main culprit here is the "( expr )" rule from "data", which allows you to nest expressions with parens, e.g. "<CODE>(10 + 5) * 3</CODE>". This causes problems for Bison, because we already use parens all over the place. Specifically, this rule conflicts with the empty "MESSAGE" from the "message" rule, "ARRAY" from "array", and "IDENT" from "type". These rules have no other symbols following them, which makes them ambiguous with respect to the "datatype" rules. Still with me? The parser will typically pick the right one, though.</P>
<P>The nested expressions rule also caused a reduce/reduce conflict. To get rid of that, I had to explicitly mention the names of the various types in the "typecast" rule, which introduces a little code duplication but it's not too bad. Just so you know, the original rule was simply:</P>
<BLOCKQUOTE><PRE>typecast
: '(' datatype ')' { $$ = $2; }
;</PRE></BLOCKQUOTE>
<P>The new rule is a little more bulky:</P>
<BLOCKQUOTE><PRE>typecast
: '(' ARRAY ')' { ... }
| '(' MESSAGE ')' { ... }
...<I>and so on for all the datatypes</I>...
;</PRE></BLOCKQUOTE>
<P>The unary minus operator is not part of the "expr" (or "data") rules, but of "integer" and "float". This also causes a shift/reduce warning.</P>
<H3>Symbol table</H3>
<P>The compiler uses two symbol tables: one for the enum symbols, and one with
the data type definitions. We need those tables to find the numeric ID/type
definition that corresponds with an identifier, and to make sure that there are
no duplicate or missing identifiers. These two symbol tables are independent,
so you may use the same identifier both as an enum symbol and a type name.</P>
<P>The compiler uses two symbol tables: one for the enum symbols, and one with the data type definitions. We need those tables to find the numeric ID/type definition that corresponds with an identifier, and to make sure that there are no duplicate or missing identifiers. These two symbol tables are independent, so you may use the same identifier both as an enum symbol and a type name.</P>
<P>The compiler does not need to keep a symbol table for the resources.
Although the combination of a resource's ID and its type code must be unique,
we can use the BResources class to check for this when we add a resource. There
is no point in duplicating this functionality in the compiler. (However, if we
are merging the new resources into an existing resource file, we will simply
overwrite duplicates.)</P>
<P>The compiler does not need to keep a symbol table for the resources. Although the combination of a resource's ID and its type code must be unique, we can use the BResources class to check for this when we add a resource. There is no point in duplicating this functionality in the compiler. (However, if we are merging the new resources into an existing resource file, we will simply overwrite duplicates.)</P>
<H3>Misc remarks</H3>
<UL>
<LI><P>As the grammar shows, the last field in an enum statement may be
followed by a comma. This is valid C/C++ syntax, and since the enum will
typically end up in a header file, we support that feature as well. For
anything else between braces, the last item may not be followed by a
comma.</P></LI>
<LI>As the grammar shows, the last field in an enum statement may be followed by a comma. This is valid C/C++ syntax, and since the enum will typically end up in a header file, we support that feature as well. For anything else between braces, the last item may not be followed by a comma.<BR>&nbsp;</LI>
<LI><P>The type code that follows the "resource" keyword may be enclosed by
parens for historical reasons. The preferred notation is without, just like in
front of field names (where no parens are allowed).</P></LI>
<LI>The type code that follows the "resource" keyword may be enclosed by parens for historical reasons. The preferred notation is without, just like in front of field names (where no parens are allowed).<BR>&nbsp;</LI>
<LI><P>Even though "ARCHIVE IDENT" is a valid data type, we simply ignore the
identifier for now. Maybe later we will support casting between different
archives or whatever. For now, casting to an archive is the same as casting to
a message, since an archive is really a message.</P></LI>
<LI>Even though "ARCHIVE IDENT" is a valid data type, we simply ignore the identifier for now. Maybe later we will support casting between different archives or whatever. For now, casting to an archive is the same as casting to a message, since an archive is really a message.<BR>&nbsp;</LI>
<LI>User-defined types and defines have their own symbol tables. Due to the way the parser is structured, we can only distinguish between types and defines by matching the identifier against both symbol tables. Here types have priority, so you could 'shadow' a define with a type name if you were really evil. Maybe it makes sense for rc to use one symbol table for all things in the future, especially since we're using yet another table for enums. We'll see.</LI>
</UL>

View File

@ -8,16 +8,22 @@
<P>This is the change history of the rc resource compiler and librdef.</P>
<P><I>Version 1.1 (not released yet):</I></P>
<P><I>Version 1.1 (December 22, 2003):</I></P>
<UL>
<LI>You can now import the contents of external files (such as pictures) into
array resources or fields. This does away with the need to convert (large)
binary files into text form.</LI>
<LI>You can now give type fields a fixed size, which can be handy for certain
kinds of string or raw data resources (for example, app_version).</LI>
<LI>Line number counter was not reset properly when compiling multiple rdef
files in one go.</LI>
<LI>You can now import the contents of external files (such as pictures) into array resources or fields. This does away with the need to convert (large) binary files into text form.</LI>
<LI>You can now give type fields a fixed size, which can be handy for certain kinds of string or raw data resources (for example, app_version).</LI>
<LI>You can now use built-in symbolic constants such as B_SINGLE_LAUNCH for setting the app_flags and app_version resources. There is no facility for defining your own symbols yet. (In case you were wondering, no, you cannot use the enum statement for that.)</LI>
<LI>Added limited support for numerical expressions. You can now do simple math like <CODE>(1 + 2) * 3</CODE>. Only a few operators are supported, you can only use integer operands, and the results are always cast to 32-bit integer as well. This should be more than enough for now; expressions can be made more powerful in the future should the need arise. You'll use this feature mostly for OR'ing and AND'ing symbolic constants (see above) together to make your scripts more readable.</LI>
<LI>Line number counter was not reset properly when compiling multiple rdef files in one go.</LI>
<LI>Bumped the API version number of librdef.so to 2.</LI>
</UL>
<P><I>Version 1.0 (February 16, 2003):</I></P>

View File

@ -6,70 +6,50 @@
<H1>The librdef library</H1>
<P>Of course, it would be cool if other applications (such as GUI resource
editors) could also import and export rdef files. That is why the bulk of rc's
functionality is implemented in a separate shared library, librdef.so.</P>
<P>Of course, it would be cool if other applications (such as GUI resource editors) could also import and export rdef files. That is why the bulk of rc's functionality is implemented in a separate shared library, librdef.so.</P>
<P>Using the library in your own applications is very simple. Here are some
quick instructions to get you started:</P>
<P>Using the library in your own applications is very simple. Here are some quick instructions to get you started:</P>
<OL>
<LI><CODE>#include "rdef.h"</CODE> in your sources</LI>
<LI>link your app to librdef.so</LI>
</OL>
<P>The API is rather bare-bones, but it gets the job done. The library uses
files to transfer data to and from your application. This may seem odd, but it
is actually a big advantage. After calling the API functions to compile an rdef
file, you can use the standard BResources class to read the resources from the
output file. Chances are high that your application already knows how to do
this.</P>
<P>The API is rather bare-bones, but it gets the job done. The library uses files to transfer data to and from your application. This may seem odd, but it is actually a big advantage. After calling the API functions to compile an rdef file, you can use the standard BResources class to read the resources from the output file. Chances are high that your application already knows how to do this.</P>
<P>To compile a resource file, the steps are typically this:</P>
<OL>
<LI>Call <CODE>rdef_add_include_dir()</CODE> one or more times to add include
file search paths.</LI>
<LI>Call <CODE>rdef_add_include_dir()</CODE> one or more times to add include file search paths.</LI>
<LI>Call <CODE>rdef_add_input_file()</CODE> one or more times to add the rdef
files that you want to compile.</LI>
<LI>Call <CODE>rdef_add_input_file()</CODE> one or more times to add the rdef files that you want to compile.</LI>
<LI>Call <CODE>rdef_set_flags()</CODE> to toggle compiler options.</LI>
<LI>Call <CODE>rdef_compile()</CODE> with the name of the output file. This
performs the actual compilation.</LI>
<LI>Call <CODE>rdef_compile()</CODE> with the name of the output file. This performs the actual compilation.</LI>
<LI>Call <CODE>rdef_free_input_files()</CODE> to clear the list of input files
that you added earlier.</LI>
<LI>Call <CODE>rdef_free_input_files()</CODE> to clear the list of input files that you added earlier.</LI>
<LI>Call <CODE>rdef_free_include_dirs()</CODE> to clear the list of include
directories that you added earlier.</LI>
<LI>Call <CODE>rdef_free_include_dirs()</CODE> to clear the list of include directories that you added earlier.</LI>
</OL>
<P>Decompiling is very similar, although include directories are not used
here:</P>
<P>Decompiling is very similar, although include directories are not used here:</P>
<OL>
<LI>Call <CODE>rdef_add_input_file()</CODE> one or more times to add the
resource files that you want to decompile.</LI>
<LI>Call <CODE>rdef_add_input_file()</CODE> one or more times to add the resource files that you want to decompile.</LI>
<LI>Call <CODE>rdef_set_flags()</CODE> to toggle compiler options.</LI>
<LI>Call <CODE>rdef_decompile()</CODE> with the name of the output file. The
name of the header file (if any) will be automatically constructed by appending
".h" to the output file name.</LI>
<LI>Call <CODE>rdef_decompile()</CODE> with the name of the output file. The name of the header file (if any) will be automatically constructed by appending ".h" to the output file name.</LI>
<LI>Call <CODE>rdef_free_input_files()</CODE> to clear the list of input files
that you added earlier.</LI>
<LI>Call <CODE>rdef_free_input_files()</CODE> to clear the list of input files that you added earlier.</LI>
</OL>
<P>If one of these functions returns something other than B_OK, an error
occurred. You can look at the following variables to find out more about the
error, and construct meaningul error messages:</P>
<P>If one of these functions returns something other than B_OK, an error occurred. You can look at the following variables to find out more about the error, and construct meaningul error messages:</P>
<UL>
<LI><CODE>rdef_err</CODE> The error code that was returned.</LI>
@ -78,11 +58,7 @@ error, and construct meaningul error messages:</P>
<LI><CODE>rdef_err_msg</CODE> The error message from the compiler.</LI>
</UL>
<P>For more information about using librdef, see "rdef.h", which explains the
available functions and data structures in more depth. For a real-world
example, take a look at "rc.cpp", which contains the complete implementation of
the rc compiler. As you'll see, there really isn't much to it, because librdef
already does all the work.</P>
<P>For more information about using librdef, see "rdef.h", which explains the available functions and data structures in more depth. For a real-world example, take a look at "rc.cpp", which contains the complete implementation of the rc compiler. As you'll see, there really isn't much to it, because librdef already does all the work.</P>
</BODY>
</HTML>

View File

@ -0,0 +1,43 @@
<HTML>
<HEAD>
<TITLE>Work notes</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<H1>Work notes</H1>
<P>These are my notes on the evolution of rc. I used to keep these in separate files on my development machine, but it makes more sense to include them here. Warning: geeky stuff ahead.</P>
<UL>
<LI>Unlike in most parsers, the unary minus operator is currently not part of the 'data' rules, but of 'integer' and 'float'. When expression support is more complete, it may make more sense to put this in 'data'. However, 'integer' is used in other places as well and these places allow negative numbers too. Maybe we can replace the calls to 'integer' with 'data' and allow the data to the integer only (by always trying to cast it, perhaps). Then you can do stuff like 'resource(10 + 1) 123;' But maybe that goes too far.<BR>&nbsp;</LI>
<LI>When filling in boolean fields of user-defined types or messages, you can do either "field = true" or "field = false". I thought it would be nice if you could also do just "field" and "!field". However, that introduced a reduce/reduce conflict in the parser. You see, the name of the field is an IDENT token, but we already have something like that in the 'type' rule of 'data'. The parser doesn't know what the IDENT is supposed to be: the name of the boolean field or the name of the type. Maybe there is a solution for this by shuffling around the parser rules a bit.<BR>&nbsp;</LI>
<LI>Support for the built-in types point, rect, and rgb_color is currently hardcoded into the decompiler. The other built-in types -- app_flags, mini_icon, etc -- are not supported at all. It would be better to use the type symbol table for this as well. Then the decompiler can also support user-defined types (although these type definitions must be provided to the decompiler somehow). This is advanced stuff that probably no one will ever use.<BR>&nbsp;</LI>
<LI>The builtin types are added to the symbol table "by hand". You can see this near the bottom of 'parser.y'. This is a bit cumbersome, so I have devised an alternative. We put the builtin type definitions in an rdef script and install this in a "standard include dir", for example: ~/config/include/rc. Before it compiles the user's rdef files, the compiler first loads all scripts from that standard folder. (This also allows us to use these rdef files for decompiling, and users can simply install their own. See above.)<BR>&nbsp;</LI>
<LI>In "auto names" mode, the decompiler currently does not use the enum symbol table. So if two resources have the same name and that name is a valid C/C++ identifier, the decompiler will add two conflicting symbols to the enum statement. This can also happen when multiple input files have conflicting resource IDs.<BR>&nbsp;</LI>
<LI>When you decompile certain apps (BeMail, Slayer) and then compile these rdef files again, the archive and message fields in the new .rsrc file are larger than the original's. I think this is because rc doesn't add the message fields as "fixedSize" (see the BMessage docs from the BeBook). This doesn't really hurt, though, since the BMessage will be properly unflattened regardless.<BR>&nbsp;</LI>
<LI>Right now, archives are treated as messages. Maybe we should give them their own type, B_ARCHIVED_OBJECT (type code 'ARCV').<BR>&nbsp;</LI>
<LI>New options, stolen from other resource compilers (rez and beres):
<UL>
<LI><B>-D --define symbol[=value]</B> set the value of symbol to value (or 1 if no value supplied)</LI>
<LI><B>--no-names</B> do not write any names in resource file</LI>
<LI><B>-h</B> write resource as C struct in a header file</LI>
<LI><B>-d</B> dump resource to stdout</LI>
</UL><BR></LI>
<LI>Attributes. I would be nice to have a tool that can take text-based descriptions of attributes and write out an attribute file. Of course, rc is the ideal candidate for this, since it already does the same for resources. However, resources and attributes are not the same. Attributes are name/data pairs. The name should be unique. They don't have IDs. They do have a type code, but it isn't used to identify the attribute. It is probably best to add a new kind of definition: attribute(). Should this statement allow only simple data, or must attributes be able to handle flattened messages, arrays, etc too? A source file should either contain resource() statements or attribute() statements, not both.<BR>&nbsp;</LI>
<LI>User-defined symbolic constants. To keep things simple, adding a #define keyword would suffice, although this always creates global symbols so there is a chance of name collisions. In addition (or instead) we can extend user-defined types to have their own (local) defines too. If we use the #define keyword, we should infer the type of the constant from the data (100 is integer, 10.5 is a float, etc). This is necessary because we don't have a separate preprocessor step like C/C++ does -- that is why our symbolic constants are typed.</LI>
</UL>
</BODY>
</HTML>

View File

@ -6,6 +6,8 @@
<H1>The rc resource compiler</H1>
<P>Version 1.1</P>
<H2>Table of contents</H2>
<UL>
@ -26,28 +28,13 @@
<H2>Introduction</H2>
<P>In the world of BeOS programming, a "resource" is data that is bundled with
your application. Typical examples are the application's icons and its
signature, but you can attach any data you want (bitmaps, text, cursors, etc).
You stuff this data into a .rsrc file that will be linked to your application
when it is compiled.</P>
<P>In the world of BeOS programming, a "resource" is data that is bundled with your application. Typical examples are the application's icons and its signature, but you can attach any data you want (bitmaps, text, cursors, etc). You stuff this data into a .rsrc file that will be linked to your application when it is compiled.</P>
<P>Because .rsrc files have a binary file format, you need to use special tools
to edit them, such as FileTypes, QuickRes, or Resourcer. Alternatively, you can
use a "resource compiler". This is a command line tool that takes a text-based
resource script and turns it into a .rsrc file.</P>
<P>Because .rsrc files have a binary file format, you need to use special tools to edit them, such as FileTypes, QuickRes, or Resourcer. Alternatively, you can use a "resource compiler". This is a command line tool that takes a text-based resource script and turns it into a .rsrc file.</P>
<P>With a resource compiler, you express your resources as ASCII text using a
special definition language, which makes the resource files much easier to edit
and maintain. You no longer need separate GUI tools to build your .rsrc files,
and you can even automate the whole process by calling the compiler from your
Makefile or Jamfile. Resource scripts will also make your life easier if you
use CVS, because CVS doesn't handle .rsrc files very well.</P>
<P>With a resource compiler, you express your resources as ASCII text using a special definition language, which makes the resource files much easier to edit and maintain. You no longer need separate GUI tools to build your .rsrc files, and you can even automate the whole process by calling the compiler from your Makefile or Jamfile. Resource scripts will also make your life easier if you use CVS, because CVS doesn't handle .rsrc files very well.</P>
<P>BeOS R5 comes with an (experimental) resource compiler called "beres", and a
corresponding decompiler called "deres". rc is an open source replacement (and
enhancement) of these tools. It is (mostly) backwards compatible, so you should
be able to compile your old .rdef files without any problems.</P>
<P>BeOS R5 comes with an (experimental) resource compiler called "beres", and a corresponding decompiler called "deres". rc is an open source replacement (and enhancement) of these tools. It is (mostly) backwards compatible, so you should be able to compile your old .rdef files without any problems.</P>
<!-- *********************************************************************** -->
@ -67,10 +54,7 @@ be able to compile your old .rdef files without any problems.</P>
<H2>Writing resource scripts</H2>
<P>Writing resource scripts is not difficult, although the syntax may take some
getting used to. A resource script is a plain text file with one or more
resource definition statements. In addition, it may contain C or C++ style
comments. By convention, resource script files have the extension ".rdef".</P>
<P>Writing resource scripts is not difficult, although the syntax may take some getting used to. A resource script is a plain text file with one or more resource definition statements. In addition, it may contain C or C++ style comments. By convention, resource script files have the extension ".rdef".</P>
<P>Here is an example of a simple resource script:</P>
@ -80,61 +64,43 @@ resource(3) 3.14;
resource(4) "hello world";
resource(5) $"0123456789ABCDEF";</PRE></BLOCKQUOTE>
<P>When compiled, this script produces a resource file with five resources. The
above example also illustrates the types of data that resources are allowed to
have: boolean, integer, floating point, character string (UTF-8), and raw data
buffer (hexadecimal).</P>
<P>When compiled, this script produces a resource file with five resources. The above example also illustrates the types of data that resources are allowed to have: boolean, integer, floating point, character string (UTF-8), and raw data buffer (hexadecimal).</P>
<P>By default, integer data is stored as a 32-bit int, and floating point data
is stored as a 4-byte float. If you want to change the way the data is stored,
you have to cast it:</P>
<P>By default, integer data is stored as a 32-bit int, and floating point data is stored as a 4-byte float. If you want to change the way the data is stored, you have to cast it:</P>
<BLOCKQUOTE><PRE>resource(6) (int8) 123;
resource(7) (double) 3.14;</PRE></BLOCKQUOTE>
<P>You can cast integer data to the following types: int8, uint8, int16,
uint16, int32, uint32, int64, uint64, ssize_t, size_t, off_t, time_t, float,
double, raw. You can cast floating point data to: float, double, raw. You are
not allowed to cast boolean, string, or raw data to other types.</P>
<P>You can cast integer data to the following types: int8, uint8, int16, uint16, int32, uint32, int64, uint64, ssize_t, size_t, off_t, time_t, float, double, raw. You can cast floating point data to: float, double, raw. You are not allowed to cast boolean, string, or raw data to other types.</P>
<P>In addition to casting, you can also change the resource's type code. This
does not change the way the data is stored, only what it is called. To change
the type code of a resource:</P>
<P>In addition to casting, you can also change the resource's type code. This does not change the way the data is stored, only what it is called. To change the type code of a resource:</P>
<BLOCKQUOTE><PRE>resource(8) #'dude' 123;</PRE></BLOCKQUOTE>
<P>This changes the type of resource 8 to the four-character-code 'dude'. If
you did not change it, it would be 'LONG', which stands for 32-bit integer. By
changing the type code, you assign a new meaning to the resource. You can also
specify type codes as decimal or hexadecimal numbers:</P>
<P>This changes the type of resource 8 to the four-character-code 'dude'. If you did not change it, it would be 'LONG', which stands for 32-bit integer. By changing the type code, you assign a new meaning to the resource. You can also specify type codes as decimal or hexadecimal numbers:</P>
<BLOCKQUOTE><PRE>resource(9) #200 123;
resource(10) #0xC8 123;</PRE></BLOCKQUOTE>
<P>For historical reasons, you may also enclose the type code in parens, but
that is not the preferred notation. Type casts and type codes can be
combined:</P>
<P>For historical reasons, you may also enclose the type code in parens, but that is not the preferred notation. Type casts and type codes can be combined:</P>
<BLOCKQUOTE><PRE>resource(11) #'dude' (int8) 123;
resource(11) (#'dude') (int8) 123;</PRE></BLOCKQUOTE>
<P>In the above examples, we have given the resources numeric IDs. The
combination of ID and type code must be unique in the resource file; you cannot
have two int32 resources with ID 1, for example. However, it is perfectly fine
(but not necessarily a good idea) to do the following, because the data types
are different:</P>
<P>In the above examples, we have given the resources numeric IDs. The combination of ID and type code must be unique in the resource file; you cannot have two int32 resources with ID 1, for example. However, it is perfectly fine (but not necessarily a good idea) to do the following, because the data types are different:</P>
<BLOCKQUOTE><PRE>resource(12) 123;
resource(12) "w00t!";</PRE></BLOCKQUOTE>
<P>For your own convenience, you can also name resources. Unlike the ID/type
code combination, names do not have to be unique. </P>
<P>For your own convenience, you can also name resources. Unlike the ID/type code combination, names do not have to be unique. </P>
<BLOCKQUOTE><PRE>resource(13, "Friday") "Bad Luck";</PRE></BLOCKQUOTE>
<P>Since it is likely that you will be using these resources from a C/C++
program, it may be convenient to refer to them by symbolic names instead of
hardcoded numeric ID's. The rdef format allows you to do this:</P>
<P>You can also do simple maths. The emphasis here is on simple because the number of operators is limited, they only work on integer data (or anything that can be cast to integer), and the result is always 32-bit integer as well. Still, the lazy amongst you may find it handy:</P>
<BLOCKQUOTE><PRE>resource(14) 2 * (4 + 3);</PRE></BLOCKQUOTE>
<P>Since it is likely that you will be using these resources from a C/C++ program, it may be convenient to refer to them by symbolic names instead of hardcoded numeric ID's. The rdef format allows you to do this:</P>
<BLOCKQUOTE><PRE>enum
{
@ -144,14 +110,9 @@ hardcoded numeric ID's. The rdef format allows you to do this:</P>
resource(R_AppName) "MyKillerApp";</PRE></BLOCKQUOTE>
<P>The compiler will automatically substitute the symbol R_AppName with the
number 1. (You don't have to start these symbol names with the prefix R_, but
it is somewhat of a convention.)</P>
<P>The compiler will automatically substitute the symbol R_AppName with the number 1. (You don't have to start these symbol names with the prefix R_, but it is somewhat of a convention.)</P>
<P>Now how do you tell your C/C++ app about these symbolic names? You simply
put the enum into a header file that you include both from your application's
source code and your rdef file. The header file, which we'll call
"myresources.h", now looks like this:</P>
<P>Now how do you tell your C/C++ app about these symbolic names? You simply put the enum into a header file that you include both from your application's source code and your rdef file. The header file, which we'll call "myresources.h", now looks like this:</P>
<BLOCKQUOTE><PRE>enum
{
@ -165,22 +126,13 @@ source code and your rdef file. The header file, which we'll call
resource(R_AppName) "MyKillerApp";</PRE></BLOCKQUOTE>
<P>Don't let the .h suffix fool you: the header file is still considered to be
an rdef file, and must contain valid rdef syntax. If you add any other C/C++
code, your resource script will fail to compile. Of course, you shouldn't add
any other rdef syntax to the header either (unless you want your C++ compiler
to start complaining). Besides comments, the only safe thing to put in that
header file is the enum statement, because both rdef and C/C++ understand
it.</P>
<P>Don't let the .h suffix fool you: the header file is still considered to be an rdef file, and must contain valid rdef syntax. If you add any other C/C++ code, your resource script will fail to compile. Of course, you shouldn't add any other rdef syntax to the header either (unless you want your C++ compiler to start complaining). Besides comments, the only safe thing to put in that header file is the enum statement, because both rdef and C/C++ understand it.</P>
<P>Just like IDs, symbolic identifiers can be combined with a name:</P>
<BLOCKQUOTE><PRE>resource(R_AppName, "AppName") "MyKillerApp";</PRE></BLOCKQUOTE>
<P>If you don't specify a name, and invoke the compiler with the
<CODE>--auto-names</CODE> option, it automatically uses the symbolic ID for the
name as well. So the ID of the following resource is 1 (because that is what
R_AppName corresponds to) and its name becomes "R_AppName":</P>
<P>If you don't specify a name, and invoke the compiler with the <CODE>--auto-names</CODE> option, it automatically uses the symbolic ID for the name as well. So the ID of the following resource is 1 (because that is what R_AppName corresponds to) and its name becomes "R_AppName":</P>
<BLOCKQUOTE><PRE>resource(R_AppName) "MyKillerApp";</PRE></BLOCKQUOTE>
@ -190,15 +142,11 @@ R_AppName corresponds to) and its name becomes "R_AppName":</P>
<H2>Big fat resources</H2>
<P>The resources we have made so far consisted of a single data item, but you
can also supply a collection of data values. The simplest of these compound
data structures is the array:</P>
<P>The resources we have made so far consisted of a single data item, but you can also supply a collection of data values. The simplest of these compound data structures is the array:</P>
<BLOCKQUOTE><PRE>resource(20) array { 1234, 5678 };</PRE></BLOCKQUOTE>
<P>An array is nothing more than a raw buffer. The above statement takes the
two 32-bit integers 1234 and 5678 and stuffs them into a new 64-bit buffer. You
can put any kind of data into an array, even other arrays:</P>
<P>An array is nothing more than a raw buffer. The above statement takes the two 32-bit integers 1234 and 5678 and stuffs them into a new 64-bit buffer. You can put any kind of data into an array, even other arrays:</P>
<BLOCKQUOTE><PRE>resource(21) array
{
@ -209,10 +157,7 @@ can put any kind of data into an array, even other arrays:</P>
$"AABB"
};</PRE></BLOCKQUOTE>
<P>It is up to you to remember the structure of this array, because array
resources don't keep track of what kind of values you put into them and where
you put these values. For that, we have messages. A message resource is a
flattened BMessage:</P>
<P>It is up to you to remember the structure of this array, because array resources don't keep track of what kind of values you put into them and where you put these values. For that, we have messages. A message resource is a flattened BMessage:</P>
<BLOCKQUOTE><PRE>resource(22) message('blah')
{
@ -222,10 +167,7 @@ flattened BMessage:</P>
"Other Msg" = message { "field" = "value" }
};</PRE></BLOCKQUOTE>
<P>A message has an optional "what" code, in this case 'blah', and one or more
fields. A field has a name (between double quotes), a value, and a data type.
By default, the field assumes the type of its data, but you can also specify an
explicit data type and type code in front of the field name:</P>
<P>A message has an optional "what" code, in this case 'blah', and one or more fields. A field has a name (between double quotes), a value, and a data type. By default, the field assumes the type of its data, but you can also specify an explicit data type and type code in front of the field name:</P>
<BLOCKQUOTE><PRE>resource(23) message('bla2')
{
@ -235,9 +177,7 @@ explicit data type and type code in front of the field name:</P>
#'dude' raw "buffer2" = $"aabbccdd" // you can also combine them
};</PRE></BLOCKQUOTE>
<P>A special type of message is the "archive". The BeOS API allows you to take
a BArchivable class and flatten it into a BMessage. You can also add such
archives to your resource scripts:</P>
<P>A special type of message is the "archive". The BeOS API allows you to take a BArchivable class and flatten it into a BMessage. You can also add such archives to your resource scripts:</P>
<BLOCKQUOTE><PRE>resource(24) #'BBMP' archive BBitmap
{
@ -251,11 +191,9 @@ archives to your resource scripts:</P>
}
};</PRE></BLOCKQUOTE>
<P>So what's this "rect" thing in the "_frame" field? Besides arrays and
messages, the compiler also supports a number of other data structures from the
BeAPI:</P>
<P>So what's this "rect" thing in the "_frame" field? Besides arrays and messages, the compiler also supports a number of other data structures from the BeAPI:</P>
<BLOCKQUOTE><TABLE BORDER="1">
<BLOCKQUOTE><TABLE BORDER=1 SUMMARY="">
<TR><TH>type</TH><TH>corresponds to</TH><TH>fields</TH></TR>
<TR><TD>point</TD><TD>BPoint, B_POINT_TYPE</TD><TD>float x, y</TD></TR>
<TR><TD>rect</TD><TD>BRect, B_RECT_TYPE</TD><TD>float left, top, right, bottom</TD></TR>
@ -266,17 +204,14 @@ BeAPI:</P>
<BLOCKQUOTE><PRE>resource(25) rgb_color { 255, 128, 0, 0 };</PRE></BLOCKQUOTE>
<P>Or you can use the field names, in which case the order of the fields does
not matter:</P>
<P>Or you can use the field names, in which case the order of the fields does not matter:</P>
<BLOCKQUOTE><PRE>resource(26) rgb_color
{
blue = 0, green = 128, alpha = 0, red = 255
};</PRE></BLOCKQUOTE>
<P>You can also make your own data structures, or as we refer to them,
"user-defined types". Suppose that your application wants to store its GUI
elements in the resources:</P>
<P>You can also make your own data structures, or as we refer to them, "user-defined types". Suppose that your application wants to store its GUI elements in the resources:</P>
<BLOCKQUOTE><PRE>type #'menu' menu
{
@ -292,12 +227,7 @@ type #'item' menuitem
bool enabled = true // default value is "true"
};</PRE></BLOCKQUOTE>
<P>A type has a name, an optional type code, and one or more fields. You are
advised not to pick a type code that already belongs to one of the built-in
types, to avoid any confusion. Each field has a data type, a name, and a
default value. If you don't specify a default, it is typically 0 or empty. To
create a new menu resource using the types from the above example, you might
do:</P>
<P>A type has a name, an optional type code, and one or more fields. You are advised not to pick a type code that already belongs to one of the built-in types, to avoid any confusion. Each field has a data type, a name, and a default value. If you don't specify a default, it is typically 0 or empty. To create a new menu resource using the types from the above example, you might do:</P>
<BLOCKQUOTE><PRE>resource(27) menu
{
@ -311,15 +241,9 @@ do:</P>
}
};</PRE></BLOCKQUOTE>
<P>Like an array, a type resource doesn't remember its internal structure. You
can regard types as fancy arrays that are easier to fill in, a template if you
will. User-defined types work under the same rules as the built-in types point,
rect, and rgb_color, so you can specify the fields in order or by their names.
If you don't specify a field, its default value will be used.</P>
<P>Like an array, a type resource doesn't remember its internal structure. You can regard types as fancy arrays that are easier to fill in, a template if you will. User-defined types work under the same rules as the built-in types point, rect, and rgb_color, so you can specify the fields in order or by their names. If you don't specify a field, its default value will be used.</P>
<P>Types can also have a default resource ID and/or name. If you omit to give
the resource an ID or a name, it uses the defaults from its data type. For
example, this:</P>
<P>Types can also have a default resource ID and/or name. If you omit to give the resource an ID or a name, it uses the defaults from its data type. For example, this:</P>
<BLOCKQUOTE><PRE>type myint { int32 i };
resource(10, "MyName") myint { 123 };</PRE></BLOCKQUOTE>
@ -329,35 +253,21 @@ resource(10, "MyName") myint { 123 };</PRE></BLOCKQUOTE>
<BLOCKQUOTE><PRE>type(10, "MyName") myint { int32 i };
resource myint { 123 };</PRE></BLOCKQUOTE>
<P>And to save you even more typing, simple types that have only one field can
also be specified as:</P>
<P>And to save you even more typing, simple types that have only one field can also be specified as:</P>
<BLOCKQUOTE><PRE>resource myint 123;</PRE></BLOCKQUOTE>
<P>Most data types have a fixed size; a uint16 is always 2 bytes long, a float
always comprises 4 bytes, and so on. But the sizes of string and raw data
resources depend on what you put in them. Sometimes you may want to force
these kinds of resources to have a fixed size as well. You can do this as
follows:</P>
<P>Most data types have a fixed size; a uint16 is always 2 bytes long, a float always comprises 4 bytes, and so on. But the sizes of string and raw data resources depend on what you put in them. Sometimes you may want to force these kinds of resources to have a fixed size as well. You can do this as follows:</P>
<BLOCKQUOTE><PRE>type fixed { string s[64] };</PRE></BLOCKQUOTE>
<P>Any resources with type "fixed" will always contain a 64-byte string, no
matter how many characters you actually specify. Too much data will be
truncated; too little data will be padded with zeroes. Note that string
resources are always terminated with a null character, so string "s" in the
above type only allows for 63 real characters. The number between the square
brackets always indicates bytes (unlike C/C++ arrays which use a similar
notation).</P>
<P>Any resources with type "fixed" will always contain a 64-byte string, no matter how many characters you actually specify. Too much data will be truncated; too little data will be padded with zeroes. Note that string resources are always terminated with a null character, so string "s" in the above type only allows for 63 real characters. The number between the square brackets always indicates bytes (unlike C/C++ arrays which use a similar notation).</P>
<P>If you have (large) binary files that you want to include in the resources,
such as pictures of Buffy, you don't need to convert the binary data to text
form first. You can simply "import" the file:</P>
<P>If you have (large) binary files that you want to include in the resources, such as pictures of Buffy, you don't need to convert the binary data to text form first. You can simply "import" the file:</P>
<BLOCKQUOTE><PRE>resource(22) #'PNG ' import "buffy.png";</PRE></BLOCKQUOTE>
<P>Imported resources are always arrays (raw data), and you can specify the
import statement everywhere that array data is valid.</P>
<P>Imported resources are always arrays (raw data), and you can specify the import statement everywhere that array data is valid.</P>
<!-- *********************************************************************** -->
@ -365,12 +275,9 @@ import statement everywhere that array data is valid.</P>
<H2>Application resources</H2>
<P>All BeOS applications (except command line apps) have a basic set of
resources, such as a MIME signature, launch flags, icons, and a few others.
Adding these kinds of resources is easy, because rc also has a number of
built-in types for that:</P>
<P>All BeOS applications (except command line apps) have a basic set of resources, such as a MIME signature, launch flags, icons, and a few others. Adding these kinds of resources is easy, because rc also has a number of built-in types for that:</P>
<BLOCKQUOTE><TABLE BORDER="1">
<BLOCKQUOTE><TABLE BORDER=1 SUMMARY="">
<TR><TH>type</TH><TH>corresponds to</TH><TH>fields</TH></TR>
<TR><TD>app_signature</TD><TD>the app's MIME signature</TD><TD>string
@ -390,12 +297,57 @@ minor, variety, internal<BR>string short_info, long_info</TD></TR>
</TABLE></BLOCKQUOTE>
<!-- examples on how to fill in these resources -->
<P>Here are some examples on how to use these resources. These things are also documented in the Storage Kit section of the BeBook, so refer to that for more information.</P>
<P>Filling in these application resources is a little tricky with the current
version of rc. A future version will make this easier, when the user-defined
types mechanism becomes more powerful. For now, see the file
<CODE>tests/builtin.rdef</CODE> for more info.</P>
<P>The signature:</P>
<BLOCKQUOTE><PRE>resource app_signature "application/x-vnd.YourName.YourApp";</PRE></BLOCKQUOTE>
<P>The application flags determine how your application is launched. You must 'OR' together a combination of the following symbols:</P>
<UL>
<LI><CODE>B_SINGLE_LAUNCH</CODE></LI>
<LI><CODE>B_MULTIPLE_LAUNCH</CODE></LI>
<LI><CODE>B_EXCLUSIVE_LAUNCH</CODE></LI>
<LI><CODE>B_BACKGROUND_APP</CODE></LI>
<LI><CODE>B_ARGV_ONLY</CODE></LI>
</UL>
<P>For example:</P>
<BLOCKQUOTE><PRE>resource app_flags B_SINGLE_LAUNCH | B_BACKGROUND_APP;</PRE></BLOCKQUOTE>
<P>The version information resource contains a number of fields for you to fill in. Most are pretty obvious, except maybe for the "variety" field. It can take one of the following values:</P>
<UL>
<LI><CODE>B_APPV_DEVELOPMENT</CODE> - development version</LI>
<LI><CODE>B_APPV_ALPHA</CODE> - alpha version</LI>
<LI><CODE>B_APPV_BETA</CODE> - beta version</LI>
<LI><CODE>B_APPV_GAMMA</CODE> - gamma version</LI>
<LI><CODE>B_APPV_GOLDEN_MASTER</CODE> - golden master</LI>
<LI><CODE>B_APPV_FINAL</CODE> - release version</LI>
</UL>
<P>For example:</P>
<BLOCKQUOTE><PRE>resource app_version
{
major = 1,
middle = 0,
minor = 0,
variety = B_APPV_BETA,
internal = 0,
short_info = "My Cool Program",
long_info = "My Cool Program - Copyright Me"
};</PRE></BLOCKQUOTE>
<P>The supported file types resource contains a list of MIME types, not unlike this:</P>
<BLOCKQUOTE><PRE>resource file_types message
{
"types" = "text/plain",
"types" = "text"
};</PRE></BLOCKQUOTE>
<!-- *********************************************************************** -->
@ -403,24 +355,15 @@ types mechanism becomes more powerful. For now, see the file
<H2>Compiling</H2>
<P>rc is a command line tool, which means you must run it from a Terminal
window. Typical usage example:</P>
<P>rc is a command line tool, which means you must run it from a Terminal window. Typical usage example:</P>
<BLOCKQUOTE><PRE>rc -o things.rsrc things.rdef</PRE></BLOCKQUOTE>
<P>This tells rc that you wish to compile the script "things.rdef" to the
resource file "things.rsrc". The default name for the output file is
"out.rsrc", but you can change that with the <CODE>-o</CODE> or
<CODE>--output</CODE> switch, just like we did here.</P>
<P>This tells rc that you wish to compile the script "things.rdef" to the resource file "things.rsrc". The default name for the output file is "out.rsrc", but you can change that with the <CODE>-o</CODE> or <CODE>--output</CODE> switch, just like we did here.</P>
<P>You can specify multiple rdef files if you wish, and they will all be
compiled into one big resource file. If your rdef files #include files that are
not in the current working directory, you can add include paths with the
<CODE>-I</CODE> or <CODE>--include</CODE> option. For a complete list of
options, type <CODE>rc --help</CODE>.</P>
<P>You can specify multiple rdef files if you wish, and they will all be compiled into one big resource file. If your rdef files #include files that are not in the current working directory, you can add include paths with the <CODE>-I</CODE> or <CODE>--include</CODE> option. For a complete list of options, type <CODE>rc --help</CODE>.</P>
<P>If your project uses a Makefile, you can have rc automatically generate
the resource file for you:</P>
<P>If your project uses a Makefile, you can have rc automatically generate the resource file for you:</P>
<BLOCKQUOTE><PRE>things.rsrc: things.rdef
rc -o $@ $^</PRE></BLOCKQUOTE>
@ -433,28 +376,15 @@ the resource file for you:</P>
<H2>Decompiling</H2>
<P>Of course you can write the resource scripts by hand, but if you already
have a .rsrc file you can tell rc to decompile it. This will produce a
ready-to-go rdef script, and save you some trouble. (Although in some cases
it may be necessary to edit the script a little to suit your needs.) Note that
rc isn't limited to just .rsrc files; you can decompile any file that has
resources, including applications.</P>
<P>Of course you can write the resource scripts by hand, but if you already have a .rsrc file you can tell rc to decompile it. This will produce a ready-to-go rdef script, and save you some trouble. (Although in some cases it may be necessary to edit the script a little to suit your needs.) Note that rc isn't limited to just .rsrc files; you can decompile any file that has resources, including applications.</P>
<P>For example, to decompile the file "things.rsrc" into "things.rdef", do:</P>
<BLOCKQUOTE><PRE>rc --decompile -o things.rdef things.rsrc</PRE></BLOCKQUOTE>
<P>The decompiler produces an rdef resource script with the name "out.rdef",
but you can change that name with the <CODE>-o</CODE> or <CODE>--output</CODE>
switches. If you specify the <CODE>--auto-names</CODE> option, rc will also
write a C/C++ header file. Any resources whose name is a valid C/C++
identifier will be added to the header file. Now your program can access the
resource using this symbolic name.</P>
<P>The decompiler produces an rdef resource script with the name "out.rdef", but you can change that name with the <CODE>-o</CODE> or <CODE>--output</CODE> switches. If you specify the <CODE>--auto-names</CODE> option, rc will also write a C/C++ header file. Any resources whose name is a valid C/C++ identifier will be added to the header file. Now your program can access the resource using this symbolic name.</P>
<P>Note: Even though rc can decompile multiple .rsrc files into one script, it
does not detect conflicts in resource names or IDs. In such cases, the
resulting .rdef and/or .h files may give errors when you try to compile
them.</P>
<P>Note: Even though rc can decompile multiple .rsrc files into one script, it does not detect conflicts in resource names or IDs. In such cases, the resulting .rdef and/or .h files may give errors when you try to compile them.</P>
<!-- *********************************************************************** -->
@ -462,11 +392,7 @@ them.</P>
<H2>Authors</H2>
<P>The rc resource compiler and its companion library librdef were written by
<A HREF="mailto:mahlzeit@users.sourceforge.net">Matthijs Hollemans</A> for the
<A HREF="http://www.openbeos.org">OpenBeOS</A> project. Thanks to Ingo Weinhold
for the Jamfile and the Jam rules. Comments, bug reports, suggestions, and
patches are welcome!</P>
<P>The rc resource compiler and its companion library librdef were written by <A HREF="mailto:mahlzeit@users.sourceforge.net">Matthijs Hollemans</A> for the <A HREF="http://www.openbeos.org">OpenBeOS</A> project. Thanks to Ingo Weinhold for the Jamfile and the Jam rules. Comments, bug reports, suggestions, and patches are welcome!</P>
<!-- *********************************************************************** -->
@ -476,23 +402,11 @@ patches are welcome!</P>
<P>Copyright (c) 2003 Matthijs Hollemans</P>
<P>Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:</P>
<P>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</P>
<P>The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.</P>
<P>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</P>
<P>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.</P>
<P>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</P>
</BODY>
</HTML>

View File

@ -1,35 +1,20 @@
<HTML>
<HEAD>
<TITLE>Planned features</TITLE>
<TITLE>Ye Olde To Do List</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<H1>Planned features</H1>
<H1>Ye Olde To Do List</H1>
<P>What will future versions of rc bring? Here are some ideas.</P>
<UL>
<LI><P>Simple integer expressions + - * / | & ~ ()</P></LI>
<LI>Attributes, so you can also use rdef scripts to add attributes to your files instead of (or in addition to) resources.<BR>&nbsp;</LI>
<LI><P>Make user-defined types more powerful. Some ideas: bitmask fields (for
app_flags), choice fields (for app_version), conditional constructs.</P></LI>
<LI>Expand the expressions to handle other data types besides integer data. This would include floating point (+ - * /), string (+), raw (+), and booleans (! &amp;&amp; ||).<BR>&nbsp;</LI>
<LI><P>Attributes, so you can also use rdef scripts to add attributes to your
files instead of (or in addition to) resources.</P></LI>
<LI><P>In "auto names" mode, the decompiler currently does not use the enum
symbol table. So if two resources have the same name and that name is a valid
C/C++ identifier, the decompiler will add two conflicting symbols to the enum
statement. This can also happen when multiple input files have conflicting
resource IDs.</P></LI>
<LI><P>Support for the built-in types point, rect, and rgb_color is hardcoded
into the decompiler. The other built-in types--app_flags, mini_icon, etc--are
not supported at all. It would be better to use the type symbol table for this
as well. Then the decompiler can also support user-defined types (although these
type definitions must be provided to the decompiler somehow.)</P></LI>
<LI><P>Right now, archives are treated as messages. Maybe we should give them
their own type, B_ARCHIVED_OBJECT (type code 'ARCV').</P></LI>
<LI>Allow users to create their own symbolic constants.</LI>
</UL>

View File

@ -62,6 +62,14 @@ static type_tab_t type_table; // symbol table for data types
static void add_user_type(id_t, type_code, char*, list_t);
typedef std::map<char*, define_t, ident_compare_t> define_tab_t;
typedef define_tab_t::iterator define_iter_t;
static define_tab_t define_table; // symbol table for defines
static bool is_type(char* name);
static define_t get_define(char* name);
static data_t make_data(size_t, type_t);
static data_t make_bool(bool);
static data_t make_int(uint64);
@ -84,12 +92,15 @@ static data_t concat_data(data_t, data_t);
static data_t cast(type_t, data_t);
static data_t unary_expr(data_t, char);
static data_t binary_expr(data_t, data_t, char);
static void add_resource(id_t, type_code, data_t);
//------------------------------------------------------------------------------
%}
%expect 1
%expect 7
%union
{
@ -117,11 +128,19 @@ static void add_resource(id_t, type_code, data_t);
%type <i> integer
%type <f> float
%type <id> id
%type <d> data array arrayfields message msgfield archive type typefield
%type <d> archive array arrayfields data expr message msgfield
%type <d> type typefield type_or_define
%type <l> msgfields typefields typedeffields
%type <F> typedeffield
%type <T> datatype typecast
%left '|'
%left '^'
%left '&'
%left '+' '-'
%left '*' '/' '%'
%right FLIP
%%
script
@ -184,7 +203,7 @@ typedeffield
$$.resize = 0;
$$.data = make_default($1);
}
| datatype IDENT '=' data
| datatype IDENT '=' expr
{
$$.type = $1;
$$.name = $2;
@ -198,7 +217,7 @@ typedeffield
$$.resize = (size_t) $4;
$$.data = resize_data(make_default($1), $$.resize);
}
| datatype IDENT '[' INTEGER ']' '=' data
| datatype IDENT '[' INTEGER ']' '=' expr
{
$$.type = $1;
$$.name = $2;
@ -208,22 +227,22 @@ typedeffield
;
resource
: RESOURCE id data ';'
: RESOURCE id expr ';'
{
add_resource($2, $3.type.code, $3);
}
| RESOURCE id TYPECODE data ';'
| RESOURCE id TYPECODE expr ';'
{
add_resource($2, $3, $4);
}
| RESOURCE id '(' TYPECODE ')' data ';'
| RESOURCE id '(' TYPECODE ')' expr ';'
{
add_resource($2, $4, $6);
}
;
id
: /* empty (causes shift/reduce conflict with typecast) */
: /* empty */
{
$$.has_id = false; $$.has_name = false; $$.name = NULL;
}
@ -277,8 +296,8 @@ array
;
arrayfields
: arrayfields ',' data { $$ = concat_data($1, $3); }
| data { $$ = $1; $$.type = get_type("raw"); }
: arrayfields ',' expr { $$ = concat_data($1, $3); }
| expr { $$ = $1; $$.type = get_type("raw"); }
;
message
@ -311,23 +330,23 @@ msgfields
;
msgfield
: STRING '=' data
: STRING '=' expr
{
$$ = $3;
$$.name = (char*) $1.ptr;
}
| datatype STRING '=' data
| datatype STRING '=' expr
{
$$ = cast($1, $4);
$$.name = (char*) $2.ptr;
}
| TYPECODE STRING '=' data
| TYPECODE STRING '=' expr
{
$$ = $4;
$$.type.code = $1;
$$.name = (char*) $2.ptr;
}
| TYPECODE datatype STRING '=' data
| TYPECODE datatype STRING '=' expr
{
$$ = cast($2, $5);
$$.type.code = $1;
@ -382,11 +401,24 @@ type
| IDENT data
{
$$ = make_type($1, make_data_list($2));
}
| IDENT
}
| type_or_define { $$ = $1; }
;
type_or_define
: IDENT
{
list_t list; list.count = 0; list.items = NULL;
$$ = make_type($1, list);
if (is_type($1))
{
list_t list; list.count = 0; list.items = NULL;
$$ = make_type($1, list);
}
else
{
define_t define = get_define($1);
$$ = cast(get_type("int32"), make_int(define.value));
free_mem($1);
}
}
;
@ -396,34 +428,52 @@ typefields
;
typefield
: IDENT '=' data { $$ = $3; $$.name = $1; }
| data { $$ = $1; }
: IDENT '=' expr { $$ = $3; $$.name = $1; }
| expr { $$ = $1; }
;
expr
: expr '+' expr { $$ = binary_expr($1, $3, '+'); }
| expr '-' expr { $$ = binary_expr($1, $3, '-'); }
| expr '*' expr { $$ = binary_expr($1, $3, '*'); }
| expr '/' expr { $$ = binary_expr($1, $3, '/'); }
| expr '%' expr { $$ = binary_expr($1, $3, '%'); }
| expr '|' expr { $$ = binary_expr($1, $3, '|'); }
| expr '^' expr { $$ = binary_expr($1, $3, '^'); }
| expr '&' expr { $$ = binary_expr($1, $3, '&'); }
| '~' expr %prec FLIP { $$ = unary_expr($2, '~'); }
| data { $$ = $1; }
;
data
: BOOL { $$ = cast(get_type("bool"), make_bool($1)); }
| integer { $$ = cast(get_type("int32"), make_int($1)); }
| float { $$ = cast(get_type("float"), make_float($1)); }
| STRING { $$ = cast($1.type, $1); }
| RAW { $$ = cast($1.type, $1); }
| array { $$ = cast($1.type, $1); }
| message { $$ = cast($1.type, $1); }
| archive { $$ = cast($1.type, $1); }
| type { $$ = cast($1.type, $1); }
| typecast BOOL { $$ = cast($1, make_bool($2)); }
| typecast integer { $$ = cast($1, make_int($2)); }
| typecast float { $$ = cast($1, make_float($2)); }
| typecast STRING { $$ = cast($1, $2); }
| typecast RAW { $$ = cast($1, $2); }
| typecast array { $$ = cast($1, $2); }
| typecast message { $$ = cast($1, $2); }
| typecast archive { $$ = cast($1, $2); }
| typecast type { $$ = cast($1, $2); }
: BOOL { $$ = cast(get_type("bool"), make_bool($1)); }
| integer { $$ = cast(get_type("int32"), make_int($1)); }
| float { $$ = cast(get_type("float"), make_float($1)); }
| STRING { $$ = cast($1.type, $1); }
| RAW { $$ = cast($1.type, $1); }
| array { $$ = cast($1.type, $1); }
| message { $$ = cast($1.type, $1); }
| archive { $$ = cast($1.type, $1); }
| type { $$ = cast($1.type, $1); }
| '(' expr ')' { $$ = $2; }
| typecast BOOL { $$ = cast($1, make_bool($2)); }
| typecast integer { $$ = cast($1, make_int($2)); }
| typecast float { $$ = cast($1, make_float($2)); }
| typecast STRING { $$ = cast($1, $2); }
| typecast RAW { $$ = cast($1, $2); }
| typecast array { $$ = cast($1, $2); }
| typecast message { $$ = cast($1, $2); }
| typecast archive { $$ = cast($1, $2); }
| typecast type { $$ = cast($1, $2); }
| typecast '(' expr ')' { $$ = cast($1, $3); }
;
typecast
: '(' datatype ')' { $$ = $2; }
;
: '(' ARRAY ')' { $$ = get_type("raw"); }
| '(' MESSAGE ')' { $$ = get_type("message"); }
| '(' ARCHIVE IDENT ')' { $$ = get_type("message"); free_mem($3); }
| '(' IDENT ')' { $$ = get_type($2); free_mem($2); }
;
datatype
: ARRAY { $$ = get_type("raw"); }
@ -438,8 +488,8 @@ integer
;
float
: FLOAT { $$ = $1; }
| '-' FLOAT { $$ = -($2); }
: FLOAT { $$ = $1; }
| '-' FLOAT { $$ = -($2); }
;
%%
@ -558,6 +608,27 @@ type_t get_type(char* name)
//------------------------------------------------------------------------------
bool is_type(char* name)
{
return type_table.find(name) != type_table.end();
}
//------------------------------------------------------------------------------
define_t get_define(char* name)
{
define_iter_t i = define_table.find(name);
if (i == define_table.end())
{
abort_compile(RDEF_COMPILE_ERR, "unknown define %s", name);
}
return i->second;
}
//------------------------------------------------------------------------------
data_t make_data(size_t size, type_t type)
{
data_t out;
@ -1248,6 +1319,77 @@ data_t cast(type_t new_type, data_t data)
//------------------------------------------------------------------------------
data_t unary_expr(data_t data, char oper)
{
data_t op = cast_to_uint32(get_type("int32"), data);
int32 i = *((int32*) op.ptr);
data_t out;
switch (oper)
{
case '~': out = make_int(~i); break;
}
free_mem(op.ptr);
return cast(get_type("int32"), out);
}
//------------------------------------------------------------------------------
data_t binary_expr(data_t data1, data_t data2, char oper)
{
data_t op1 = cast_to_uint32(get_type("int32"), data1);
data_t op2 = cast_to_uint32(get_type("int32"), data2);
int32 i1 = *((int32*) op1.ptr);
int32 i2 = *((int32*) op2.ptr);
data_t out;
switch (oper)
{
case '+': out = make_int(i1 + i2); break;
case '-': out = make_int(i1 - i2); break;
case '*': out = make_int(i1 * i2); break;
case '/':
if (i2 == 0)
{
abort_compile(RDEF_COMPILE_ERR, "division by zero");
}
else
{
out = make_int(i1 / i2);
}
break;
case '%':
if (i2 == 0)
{
abort_compile(RDEF_COMPILE_ERR, "division by zero");
}
else
{
out = make_int(i1 % i2);
}
break;
case '|': out = make_int(i1 | i2); break;
case '^': out = make_int(i1 ^ i2); break;
case '&': out = make_int(i1 & i2); break;
}
free_mem(op1.ptr);
free_mem(op2.ptr);
return cast(get_type("int32"), out);
}
//------------------------------------------------------------------------------
void add_resource(id_t id, type_code code, data_t data)
{
if (!id.has_id)
@ -1532,6 +1674,17 @@ static void add_file_types()
//------------------------------------------------------------------------------
static void add_define(char* name, int32 value)
{
define_t define;
define.name = name;
define.value = value;
define_table.insert(make_pair(define.name, define));
}
//------------------------------------------------------------------------------
void init_parser()
{
add_builtin_type(B_BOOL_TYPE, "bool");
@ -1563,6 +1716,19 @@ void init_parser()
add_large_icon();
add_mini_icon();
add_file_types();
add_define("B_SINGLE_LAUNCH", 0x0);
add_define("B_MULTIPLE_LAUNCH", 0x1);
add_define("B_EXCLUSIVE_LAUNCH", 0x2);
add_define("B_BACKGROUND_APP", 0x4);
add_define("B_ARGV_ONLY", 0x8);
add_define("B_APPV_DEVELOPMENT", 0x0);
add_define("B_APPV_ALPHA", 0x1);
add_define("B_APPV_BETA", 0x2);
add_define("B_APPV_GAMMA", 0x3);
add_define("B_APPV_GOLDEN_MASTER", 0x4);
add_define("B_APPV_FINAL", 0x5);
}
//------------------------------------------------------------------------------
@ -1574,10 +1740,14 @@ void clean_up_parser()
// so we don't need to free them here; compile.cpp already does that
// when it cleans up. However, we do need to remove the entries from
// the tables, otherwise they will still be around the next time we are
// asked to compile something. In DEBUG mode, however, we do free these
// items, so they don't show up in the mem leak statistics.
// asked to compile something.
#ifdef DEBUG
// Note that in DEBUG mode, we _do_ free these items, so they don't show
// up in the mem leak statistics. The names etc of builtin items are not
// alloc_mem()'d but we still free_mem() them. Not entirely correct, but
// it doesn't seem to hurt, and we only do it in DEBUG mode anyway.
for (sym_iter_t i = symbol_table.begin(); i != symbol_table.end(); ++i)
{
free_mem(i->first);
@ -1600,6 +1770,7 @@ void clean_up_parser()
symbol_table.clear();
type_table.clear();
define_table.clear();
}
//------------------------------------------------------------------------------

View File

@ -54,7 +54,7 @@ void free_ptr_list(ptr_list_t& list)
int32 rdef_get_version()
{
return 1;
return 2;
}
//------------------------------------------------------------------------------

View File

@ -7,5 +7,6 @@
#include "array.rdef"
#include "message.rdef"
#include "type.rdef"
#include "expr.rdef"
//#include "defaults.rdef" // test separately

View File

@ -36,11 +36,11 @@ type(1, "BEOS:APP_SIG") #'MIMS' app_signature
/* Application launch flags */
type(1, "BEOS:APP_FLAGS") #'APPF' app_flags
{
// 0x00000000 B_SINGLE_LAUNCH
// 0x01000000 B_MULTIPLE_LAUNCH
// 0x02000000 B_EXCLUSIVE_LAUNCH
// 0x04000000 B_BACKGROUND_APP
// 0x08000000 B_ARGV_ONLY
// B_SINGLE_LAUNCH (0x0)
// B_MULTIPLE_LAUNCH (0x1)
// B_EXCLUSIVE_LAUNCH (0x2)
// B_BACKGROUND_APP (0x4)
// B_ARGV_ONLY (0x8)
uint32 flags
};
@ -54,8 +54,12 @@ type(1, "BEOS:APP_VERSION") #'APPV' app_version
uint32 middle,
uint32 minor,
// 0 = development 1 = alpha 2 = beta
// 3 = gamma 4 = golden master 5 = final
// B_APPV_DEVELOPMENT (0)
// B_APPV_ALPHA (1)
// B_APPV_BETA (2)
// B_APPV_GAMMA (3)
// B_APPV_GOLDEN_MASTER (4)
// B_APPV_FINAL (5)
uint32 variety,
uint32 internal,

View File

@ -0,0 +1,62 @@
resource(10000) 0x80 | 0x01; // 129 (0x00000081)
resource(10001) ~0x01; // -2 (0xFFFFFFFE)
resource(10002) 0x3F1 & ~0x01; // 1008 (0x000003F0)
resource(10010) 100 + 10 + 1; // 111
resource(10011) 100 + 10 + -1; // 109
resource(10012) -15 + -15; // -30
resource(10013) -15 - -15; // 0
//resource(10014) -15 - +15; // syntax error
resource(10015) 2 * (4 + 3); // 14
resource(10020) 10 + 5 * 3; // 25
resource(10021) (10 + 5) * 3; // 45
resource(10022) 10 / 1 * 0; // 0
//resource(10030) 10 / 0; // div by 0 error
//resource(10031) 10 % 0; // div by 0 error
//resource(10032) 10 / (1 - 1); // div by 0 error
//resource(10040) 10.1 + 3.14; // cannot cast
resource(10041) (int8) 100 + (int8) 257; // 101 (32-bit)
resource(10042) ((int8) 100) + ((int8) 257); // 101 (32-bit)
resource(10043) (int8) ((int8) 100 + (int8) 257); // 101 (8-bit)
/* don't try this at home kids */
type #'LONG' my_int32 { int32 x = 10 };
resource(10050) my_int32; // 10
resource(10051) my_int32 6; // 6
resource(10052) my_int32 + 10; // 20
resource(10053) my_int32 6 + 10; // 16
resource(10054) my_int32 { 6 } + 10; // 16
resource(10056) my_int32 (int8) 257; // 1
resource(10057) my_int32 (6 + 10); // 16
type sumtin { int32 x = 10 + 5 };
resource(10060) sumtin; // 15
resource(10061) #'LONG' array { 10 + 5, 0xFF & 0x88 }; // 0x0F00...0088
resource(10062) #'LONG' array
{
(int8) (10 + 5), (int8) (0xFF & 0x88) // 0x0F88
};
resource(10063) message { "field" = (10 + 5)*3 }; // 45
//resource(10070) (array); // parse error
//resource(10071) my_int32 (my_int32); // parse error
//resource(10072) my_int32(my_int32(my_int32)); // parse error
//resource(10073) array array; // parse error
//resource(10074) array (array); // parse error
resource(10080) my_int32 my_int32; // 10
resource(10081) my_int32 my_int32 my_int32; // and so on
//------------------------------------------------------------------------------
resource(10100) B_SINGLE_LAUNCH;
resource(10101) (int8) B_EXCLUSIVE_LAUNCH;
resource(10102) B_MULTIPLE_LAUNCH | B_BACKGROUND_APP | B_ARGV_ONLY;