.rss
This section details the source text resource file format, and is a useful reference for developers in C++ and OPL.
Topics covered include; the lexical conventions obeyed, the C++ pre-processor statements supported, and a description of the native resource compiler statements.
Note the following changes since v5:
UTF8 encoding can be used in source files to represent Unicode values.
TEXT
resources are deprecated: you should use LTEXT
instead.
Binary compatibility is broken with Unicode resource files prepared with v5. For LTEXT16
, TEXT16
and BUF16
values, the data is aligned to a multiple of 2 bytes from the start of the individual resource (not the offset in the overall resource file) by inserting a padding byte (value 0xAB). This padding byte goes after the length byte for the LTEXT16
case, and there is a BAFL panic 15
in debug builds if the padding byte is not found when TResourceReader::ReadTPtrC16()
expects one.
The < number > syntax for using special character codes expects a Unicode character value if a Unicode resource is being created. The < number > syntax cannot be used to specify individual 8-bit values in Unicode resource files.
Whitespace, except when enclosed in quotes for strings, is ignored by the compiler.
Both C-style (/* */
) and C++-style (//
) comments are supported.
Strings must be enclosed with double-quotes.
To include a double-quote within a string, enter a double-quote preceded by a backslash. The following resource generates the output string "text"
: i.e., the binary sequence: 0x22 0x74 0x65 0x78 0x74 0x22
.
RESOURCE SIMPLE quote_example
{
string="\"text\"";
}
where string is of type BUF
.
To include a backslash within a string, enter two backslash characters. The following resource generates the output string \text\
: i.e., the binary sequence: 0x5c 0x74 0x65 0x78 0x74 0x5c
.
RESOURCE SIMPLE backslash_example
{
string="\\text\\";
}
You can use the syntax <
number
>
, to specify a character by character code. If a Unicode resource is being created, this must be a Unicode character value.
The following pre-processor statements are supported by the resource compiler:
#define
#undef
#line
#include
#ifdef
#ifndef
#if
#else
#endif
The resource compiler supports conditional compilation such as
#ifdef SOMETHING
// do something
#else
// do something else
#endif
or
#ifndef WHATEVER
// do something
#endif
You can include files within the source file by using a #include
directive, for example:
#include <eikdef.rh>
or
#include "eikdef.hrh"
The searching algorithm used by the resource compiler depends on whether the item to be included is enclosed in double-quotes or angled brackets.
If the filename is enclosed in double-quotes, the resource compiler searches for that file through the following directories in the given order:
the current directory
the relative directory ..\inc
the absolute directory \epoc32\include
If the filename is enclosed in angled brackets, the resource compiler searches for that file through the following directories in the given order:
the relative directory ..\inc
the absolute directory \epoc32\include
source-file
:
statement-list
A source file consists of any number of statements.
statement
:
struct-statement
| resource-statement
| character_set-statement
| name-statement
| offset-statement
| system-statement
| enum-statement
The most usual forms of statement are a struct-statement
and a resource-statement
. The struct statement defines a type of resource, see STRUCT statement. The resource statement defines an actual resource instance of the type defined in a struct, gives it a resource ID, see Generated resource IDs, and causes it to be generated in the resource file, see RESOURCE statement.
By default, the resource IDs in the generated header file are allocated from 1
in ascending sequence. The sequence can be made to begin from another value using the NAME
statement, see NAME statement.
struct-statement
:
STRUCT
struct-name
[ BYTE
| WORD
] {
struct-member-list
}
A STRUCT
statement defines the format of a struct; all resources are defined in terms of structs. The struct has a name designated by struct-name
, and some items designated by struct-member-list
.
The length of most structs is variable. A resource’s entire length can be deduced from the position of the next resource; this information is available in a resource file’s index. If the struct is embedded within a resource, however, its length must be identified another way.
Use BYTE
to prefix the struct with a single byte indicating its length (zero to 255 characters).
Use WORD
to prefix the struct with a word indicating its length (zero to 65,535 characters).
The following are valid STRUCT
statements:
STRUCT STRING
{
LTEXT str;
}
STRUCT TEST BYTE
{
WORD status;
STRUCT text;
}
The first example defines a struct called STRING
which contains a single LTEXT
item.
The second example defines a struct called TEST
which contains a WORD
and an embedded struct. The total length of TEST
is unknown, but should not exceed 255 characters: the BYTE
directive in the definition indicates that, when this struct is embedded within another, it should be preceded with a byte indicating its length (zero to 255 characters).
The struct-member-list
is a sequence of struct-member
s where each member is terminated by a semi-colon. The complete list is enclosed within a pair of braces as demonstrated by the struct called TEST
.
The following rules must be observed for struct-names
:
they must be given in upper case
they must begin with an alphabetic character, although subsequent characters may be numeric
they may not begin with any of the following resource compiler keywords — the 12 struct type-names
, see Defining struct members, and the keywords: GLOBAL
, STRUCT
, LEN
or RESOURCE
. This restriction applies only to the struct-name
, not the member-name
of any individual struct members.
So the following struct definition will generate an error
STRUCT BUFTHING
{
BUF buffer;
}
whereas this struct definition will compile correctly
STRUCT ABUFTHING
{
BUF buffer;
}
struct-member
:
member-declaration
[ =
initialiser
] ;
member-declaration
:
type-name
member-name
| [ LEN
[ BYTE
] ] type-name
member-name
[ (
length-limit
)
]
[ [
[array-size
] ]
]
type-name
:
BYTE
| WORD
|
LONG
| DOUBLE
| TEXT
|
LTEXT
|
BUF
| BUF<n>
| LINK
| LLINK
|
SRLINK
| STRUCT
Each member of a struct is identified by a declaration
followed by an optional initialiser
, and then a semi-colon.
The simplest form of declaration
is to specify a type-name
and a member-name
only:
STRUCT TEST
{
WORD length;
}
The type-name
must be in upper case (WORD
in this example).
The member-name
must be in lower case (length
in this example).
An SRLINK
member may not have an initialiser
since it is self-referencing and, therefore, only takes the value of the resource ID the struct is declared in. This is assigned automatically by the resource compiler.
These are the valid types:
|
Numbers more than one byte long are stored little-endian: that is, their least significant byte is stored first.
LINK
and STRUCT
are not type-safe: it is the responsibility of the user of a struct to ensure that the correct resource type is linked or included. The definer of a struct should give some idea, perhaps using a comment:
STRUCT TEST
{
WORD length;
STRUCT text; // should be a STRING
}
a length-limit
may be specified for members of string type (BUF
, TEXT
and LTEXT
): the length-limit
is enclosed in parentheses after the member name. In the following example
STRUCT TEST
{
TEXT string1(9);
LTEXT string2(MAX);
BUF string3;
}
string1
has a maximum of 9 characters, string2
a maximum of MAX
characters (where MAX
has been #defined
elsewhere) and string3
has no maximum length. Note that this maximum applies only to the actual characters and does not take account of the terminating zero (for TEXT
members) or leading byte (for LTEXT
members).
Note that from v5 onwards, TEXT
is deprecated.
An alternative method to using length-limit
with a BUF
type is to use the type BUF<n>
. For example, given the definition of TEST
and the resource definitions for test1
, test2
and test3
:
STRUCT TEST
{
BUF<4> string;
}
RESOURCE TEST test1
{
string="abcd";
}
RESOURCE TEST test2
{
string="ab";
}
RESOURCE TEST test3
{
string="abcdef";
}
test1
generates 0x61 0x62 0x63 0x64
test2
generates 0x61 0x62
but test3
generates an error because the maximum length of string
permitted by the definition of the STRUCT
is 4.
Recall the member-declaration
syntax; look at a specific subset of this (see Defining struct members for the full syntax of a struct member:
member-declaration
:
type-name
member-name
| [ LEN
[ BYTE
] ] type-name
member-name
[
[array-size
] ]
A declaration of a struct member may either be a simple type-name
member-name
, or it may be an array of values of identical type.
Arrays are always indicated by square brackets:
STRUCT HAS_ARRAY
{
STRUCT elements[];
}
In the example above, the HAS_ARRAY
struct has one array member, elements
. Each member of elements
is of STRUCT
type.
If you specify the array size, inside the square brackets, then the generated resource will contain no count of the number of elements. So this resource:
STRUCT FIXED_ARRAY
{
WORD elements[3];
}
RESOURCE FIXED_ARRAY example1
{
elements={9,8,7};
}
will generate the output
0x09 0x00 0x08 0x00 0x07 0x00
For variable length arrays a count of the number of elements precedes the resource. The default for this is a word, but by prefixing the struct definition with LEN
BYTE
it will be a byte count. So the following resource:
STRUCT VAR_ARRAY
{
WORD elements [];
}
RESOURCE VAR_ARRAY example2
{
elements={9,8,7};
}
will generate the output
0x03 0x00 0x09 0x00 0x08 0x00 0x07 0x00
whereas this resource:
STRUCT VAR_ARRAY2
{
LEN BYTE WORD elements[];
}
RESOURCE VAR_ARRAY2 example3
{
elements={9,8,7};
}
will generate this output
0x03 0x09 0x00 0x08 0x00 0x07 0x00
The compiler allows you to prefix LEN
BYTE
or LEN
WORD
even for fixed length arrays but it has no effect. Fixed length arrays do not generate an element count.
See also the section on initialising array items in Initialising resource members.
resource-statement
:
RESOURCE
struct-name
[resource-name
] {
resource-initialiser-list
}
The RESOURCE
statement is used to generate a resource in the resource file. The statement specifies three things:
a struct-name
which denotes the structure that will be used for the resource
It must have been defined in a previous STRUCT
statement and must be in upper case.
an optional resource-name
which identifies the resource
The resource-name
must be in lower case.
The resource-name
causes a symbolic constant to be generated in the resource compiler’s output header file, so that a resource
RESOURCE TEST my_test { /* etc */ }
will result in a definition of the form
#define MY_TEST 1
in the generated header file.
The default ID for the first resource defined in the file is one with subsequent resources’ IDs generated in ascending sequence. See Generated resource IDs for more information on how resource IDs are generated.
If no resource name is specified then the resource is generated in the object
file and assigned a resource ID as usual, but will not be published in the header file. Anonymous resources are used mainly for playback scripts where the resources are read in sequentially by default, so that assigning names to them would be superfluous.
The resource-name
may also be used by the resource compiler for LINK
and LLINK
members — see Resource identifiers for LINKs and LLINKs.
initialisation for members of the resource struct, where their default values are not appropriate
As an example, given the struct definition
STRUCT NCEDIT
{
WORD current;
WORD low;
WORD high=65535;
}
you could define a resource:
RESOURCE NCEDIT MEMORY_SIZE
{
low=640;
high=1024;
}
Thus, in the resource file, current
has the value compiler default value of 0, low
has the value 640 (specified in the resource definition) and high
has the value 1024 (specified in the resource definition, overriding the default for the struct type).
The resource initialiser has different forms depending on whether a single, simple, member is being initialised, or whether a struct or an array is being initialised:
resource-initialiser
: member-name
[ (
length-limit
)
] =
initialiser
initialiser
:
simple-initialiser
| struct-initialiser
| array-initialiser
Resource members may be initialised by default (in the struct definition) or explicitly initialised (in the resource definition).
In general,
If a member is initialised in a RESOURCE
statement, that is its value.
If it is initialised in a STRUCT
statement, that is its value for all resources which do not explicitly initialise it.
If it is neither initialised in a RESOURCE
statement nor a STRUCT
statement, then: BYTE
, WORD
and DOUBLE
members contain zero; TEXT
, LTEXT
and BUF
members contain an empty string.
It is an error for LINK
and LLINK
members to have no explicit value, so they must be initialised, either by default (in the STRUCT
statement) or explicitly (in the RESOURCE
statement).
SRLINK
members may not be initialised (either in the STRUCT
statement or a RESOURCE
statement) since they are automatically assigned the resource ID of the resource in which they appear.
Members which are themselves STRUCT
s may not be default initialised. They can only be initialised in the RESOURCE
definition. If they are not explicitly initialised they will take up zero bytes in the resource file. For example, given the following STRUCT
definition:
STRUCT TEST2
{
WORD value;
STRUCT tester;
}
The following RESOURCE
statement only generates the two bytes 0xFF 0x00
.
RESOURCE TEST2 item
{
value=255;
}
Default values can be specified for struct members by using an initialiser, for example
STRUCT NCEDIT
{
WORD current;
WORD low;
WORD high=65535;
}
With this specification, any NCEDIT
resource will, by default, have a high
member whose value is 65,535. If the RESOURCE
statement specifies a different value, it will be used instead.
simple-initialiser
:
number
| string
| resource-identifier
A BYTE
, WORD
, LONG
or DOUBLE
member must be initialised with a number. The number can be the result of simple expressions such as 3+1
or NUM1+NUM2
where NUM1
and NUM2
have been #defined
earlier on in the file.
A TEXT
, LTEXT
or BUF
member must be initialised with a string. If a length-limit
has been declared for that member in the STRUCT
definition then an error will be generated if the string
is longer than the limit. You can, however, override any maximum length declared in the struct definition by specifying a new maximum length after the member name in the resource definition. For instance the following resource
RESOURCE THING
{
string(20)="Very long string";
}
will compile correctly even if the struct definition specified a maximum length of 10 for string
.
A LINK
or LLINK
member must be initialised with a resource-identifier
. This resource identifier may be in the form of a resource name or a number.
If it is a resource name and it is declared in lower case, it refers to a resource in the file.
It is not necessary for the resource to be defined at the time the statement is processed, but an error will result if the resource is not defined in the entire file. When declared in lower case any NAME
specified in the file will be added onto the resource ID.
If the resource name is declared in upper case, it refers to a resource defined in another file.
To reference it you must #include
the relevant header file containing the resource. All resource names are turned into upper case when their #define
s are generated in the header file — this is how the resource compiler recognises that it must look for the resource in another file. If this resource does not exist in any #include
d file then an error is generated.
The resource identifier may also be specified as a number: in this case it is the resource ID (including NAME
).
If no such resource ID exists either in the file or any #include
d header files, no error is generated by the compiler. The programmer must therefore ensure that the ID is a valid reference.
See Generated resource IDs for information on how resource IDs are generated. See also NAME statement and related statements.
array-initialiser
:
{
array-initialiser-item-comma-list
}
array-initialiser-item
:
initialiser
To initialise a member of array type, give the items in the array in sequence. Each initialiser must be of a type compatible with the member being initialised.
If the member was declared as a fixed-length array (e.g., WORD elements[10]
), then it is an error to specify any more items than were given in the length (either in the STRUCT
or the RESOURCE
definition). If fewer items are specified in the default initialisation (i.e. in the STRUCT
definition) then an error also results. Note that if fewer elements are specified when initialising the array in the RESOURCE
statement, then any elements not specified after the specified values will be lost, even if they have been default initialised in the STRUCT
definition. Take the following example:
STRUCT SAMPLE
{
BYTE bts[3]={1,2,3};
}
In the following resource:
RESOURCE SAMPLE default
{}
the output will be the whole default array
0x01 0x02 0x03
but in this resource:
RESOURCE SAMPLE first_specified
{
bts={5};
}
the output is:
0x05
with the second and third elements lost. If you specify only the second element in the RESOURCE
definition then the first element is taken from the default initialisation, the second from the explicit initialisation and the third element is lost. The following resource:
RESOURCE SAMPLE second_specified
{
bts[1]=5;
}
results in the 2-byte output:
0x01 0x05
If, however, you explicitly initialise an element in the middle of an array without having supplied default values for array members before it, then an error will result.
If the array was not declared fixed-length then the number of elements is worked out from the initialis
er and prepended to the resource. The default for this element count is a word: you may specify it as a byte by declaring LEN
BYTE
in front of the array declaration in the STRUCT
definition; e.g.
STRUCT VAR_ARRAY
{
LEN BYTE WORD [];
}
You may initialise array elements with expressions. You must explicitly initialise each member component of the array otherwise the expressions will be evaluated incorrectly. The following resource:
RESOURCE SAMPLE correct_expression
{
bts[0]=3+1;
bts[1]=2;
bts[2]=3;
}
will generate the correct output 0x0
4 0x02 0x03
. However, if you use the following syntax:
RESOURCE SAMPLE incorrect_expression
{
bts={3+1,2,3};
}
the output will be 0x03 0x02 0x03.
This is because the pre-processor treats ‘3+1’
as a literal string which is then interpreted by the compiler as 3. In the resource correct_expression
above the ‘=‘ sign forces the pre-processor to evaluate the expression.
STRUCT
itemsstruct-initialiser
:
struct-name
{
struct-initialiser-item-list
}
struct-initialiser-item
:
member-name
=
initialiser
;
STRUCT
members may only be initialised in the resource definition.
To initialise a member of STRUCT
type, give the struct-name
with which you wish to initialise it, and then specify each member of that struct which you wish to initialise.
The member-name
s listed must be members of the struct-name
struct. Each initialise
must be of a type compatible with the member it is initialising.
The compiler does not enforce type safety. Any struct can be used to initialise a member declared to be of struct type. Usually, however, the designer of the struct will have intended only one or a limited number of structs ever be used to initialise a member. You should ensure that you initialise struct members with the intended struct type.
Given the previously defined struct types
STRUCT STRINGCOUNT
{
BUF message;
WORD num;
}
STRUCT SAMPLE
{
WORD anynumber;
STRUCT text; // should be a STRINGCOUNT
}
the following example shows how to define the struct within a resource:
RESOURCE SAMPLE show_how
{
anynumber=10;
text=STRINGCOUNT
{
message="Hello"
num=5;
};
}
Note the trailing semicolon after the closing }
of the text
struct member initialisation: this is because a semicolon follows all member initialisations. For longer, more complicated resources deciding where semicolons must be placed may become confusing. As shown in the following example, array lists are separated by commas and terminated without a semi-colon. Similarly, resources are not terminated by a semi-colon.
RESOURCE DIALOG example_dialog
{
title="Example";
topsection=DLAY_SECTION
{
control_list=
{
ACLIST
{
butlist_flags=BUTLIST_HORIZONTAL;
rid=R_HCIL_BUTTONS_OK_CANCEL;
}, // array member so comma needed
ACLIST
{
butlist_flags=BUTLIST_HORIZONTAL;
rid=R_HCIL_BUTTONS_OK_CANCEL;
} // last item in array so nothing needed
}; // end of struct member control_list so ‘;’ needed
}; // end of struct member topsection so ‘;’ needed
} // end of resource so nothing needed
The rules to remember are as follows:
|
See the definitions of resource-statement
, struct-statement
, struct-member
and array-initialiser
for a complete explanation of the syntax.
name-statement
:
NAME
short-name
Use this statement to ensure that the resources in the file have a unique ID so that an application can use multiple resource files without resource ID conflict.
This statement must be the first non-comment statement in the file.
The short-name
must be between one and four alphabetic characters long and be unique (no other file may use the same one). For the sake of consistency this constant should be in upper case although lower case is allowed (and will be converted to upper case by the compiler). This short-name
is then converted into a number and shifted onto the leading 20 bits of the resource ID, leaving the bottom 12 bits for the number of the resource in the file. This allows a maximum of 4095 resources to be defined in a source file.
So with NAME
set to AAAA
, if this resource is the first resource in the file
RESOURCE STRING one
{
wd=5;
}
the first entry in the generated header file will be
#define ONE 0x04FD8001
04FD8 is the leading 20 bits for all IDs in the file and 001 is the reference of that resource within the file.
Since the NAME
statement maps all resource IDs within the file onto 32-bit numbers, only LLINK
s may be used for resource references in that file. Using the NAME
statement in a resource file means that any attempt to use a LINK
will generate an error.
character_set-statement
:
CHARACTER_SET
character_set-name
Use a CHARACTER_SET
statement to define the character set to be used. The permitted values for character_set-name
are defined as:
character_set-name
:
| CP1252
|
UTF8
If the CHARACTER_SET
statement is omitted from a resource file, character set CP1252 is taken as default.
enum-statement
:
enum
[ enum-label
] {
enum-list
}
;
| ENUM
[ enum-label
] {
enum-list
}
;
Use an enum
(or an ENUM
) statement to define a set of integer values. The values are associated with symbols defined in the enum-list
; the syntax and the semantics are compatible with those of C++ enumerations.
Each member of the enum-list
is followed by a comma except for the last one. The syntax of a member is defined as:
enum-member
:
member-name
[ =initialiser
]
The defined enumerator symbols can be used in both C++ code and resource scripts and are commonly defined in files which have the conventional file extension hrh
. The .hrh
files are included in both C++ files and resource source files.
For example, the enum
definition:
enum
{
EExampleCmdIdFirst=0x100,
EExampleCmdIdSecond,
EExampleCmdIdThird,
EExampleCmdIdFourth
};
defines the enumerators EExampleCmdIdFirst
, EExampleCmdIdSecond
etc. and assigns values to them.
In general, each enumerator can be assigned a specific value. If no value is explicitly assigned, the value generated by the resource compiler is the value of the previous enumerator plus one. Thus, in the above example, EExampleCmdIdFirst
is assigned the value 0x100
(decimal 256), EExampleCmdIdSecond
is assigned the value 0x101
(decimal 257) etc.
If the first enumerator is not assigned an explicit value, it defaults to 0
.
More than one enumerator may be assigned an explicit value.
The assigned value can be coded in either hexadecimal or plain decimal notation.
enum {
testvalue1=10,
testvalue2,
testvalue3=20,
testvalue4
};
STRUCT TEST1
{
BYTE b1;
BYTE b2;
BYTE b3;
BYTE b4;
}
RESOURCE TEST1 test
{
b1=testvalue1;
b2=testvalue2;
b3=testvalue3;
b4=testvalue4;
}
In this example the resource generated is: 0x0A 0x0B 0x14 0x15
Note that the final semi-colon in an enum
may be omitted; however, to retain compatibility with the C++ compiler, it is advisable to retain it.
All resource files have the following format:
|
where:
|
Note that for LTEXT16
, TEXT16
and BUF16
values, the data is aligned to a multiple of 2 bytes from the start of the individual resource (not from the start of the overall resource file) by adding a padding byte, whose value is 0xAB
(for the LTEXT16
case, this is inserted between the length byte and the first Unicode character). The padding byte is added if the offset from the start of the individual resource's binary data (in its in-memory format) to the first Unicode character would otherwise be an odd number.
So for example the following source file:
STRUCT SIMPLE
{
WORD wd;
LONG lg;
BUF name;
}
RESOURCE SIMPLE one
{
wd=5;
lg=10000;
name="Simon";
}
RESOURCE SIMPLE two
{
name="John";
}
when compiled, will produce the following output resource file:
0: 19 00 06 00 05 00 10 27 00 00 53 69 6d 6f 6e 00 .......’ ..Simon.
10: 00 00 00 00 00 4a 6f 68 6e 04 00 0f 00 19 00 .....Joh n......