Symbian Developer Library

SYMBIAN OS V6.1 EDITION FOR C++

[Index] [Glossary] [Previous] [Next]



Resource file format .rss


Overview

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.

[Top]


Changes since v5

Note the following changes since v5:

[Top]


Resource file definition


Lexical conventions


C++ pre-processor statements

The following pre-processor statements are supported by the resource compiler:

Conditional compilation in resource files

The resource compiler supports conditional compilation such as

#ifdef SOMETHING

// do something

#else

// do something else

#endif

or

#ifndef WHATEVER

// do something

#endif

Include files within a source file

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:

If the filename is enclosed in angled brackets, the resource compiler searches for that file through the following directories in the given order:


Resource file statements

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.


Generated resource IDs

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-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.

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-members 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.

Struct names

The following rules must be observed for struct-names:

STRUCT BUFTHING
 {
 BUF buffer;
 }

STRUCT ABUFTHING
 {
 BUF buffer;
 }

Defining struct members

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

STRUCT TEST
 {
 WORD length;
 }

These are the valid types:

BYTE

A single byte. Can be treated as a signed integer (–128 to +127) or unsigned integer (0 to 255).

WORD

Two bytes. Can be treated as a signed integer (–32,768 to +32,767) or unsigned integer (0 to 65,535).

LONG

Four bytes. Can be treated as a signed integer (–2,147,483,648 to 2147483647) or an unsigned integer (0 to 4,294,967,295).

DOUBLE

Eight byte real for double precision floating point numbers (approximately ±1.7E±308).

TEXT

A string, terminated by a null. This is deprecated: use LTEXT instead.

LTEXT

A Unicode string with a leading byte which holds the length of the string, and no terminating null.

BUF

A Unicode string with no terminating null and no leading byte.

BUF8

A string of 8-bit characters, with no terminating null and no leading byte count. Used for putting 8-bit data into a resource.

BUF<n>

A Unicode string with no terminating null and no leading byte but which has a maximum length n. This type is an alternative to using the length-limit with BUF types,

LINK

The ID of another resource (16 bits), rather like a pointer to that resource.

LLINK

The ID of another resource (32 bits).

SRLINK

A self-referencing link. This is a 32 bit link which contains the resource ID of the resource it is defined in.

STRUCT

Any struct, rather like including that struct as a member in this struct. STRUCT members are useful because it means that once a struct has been defined it can be re-used for a variety of resources.

STRUCT TEST
 {
 WORD length;
 STRUCT text; // should be a STRING
 }

STRUCT TEST
 {
 TEXT string1(9);
 LTEXT string2(MAX);
 BUF string3;
 }

STRUCT TEST
 {
 BUF<4> string;
 }

RESOURCE TEST test1
 {
 string="abcd";
 }

RESOURCE TEST test2
 {
 string="ab";
 }

RESOURCE TEST test3
 {
 string="abcdef";
 }

Arrays within structs

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-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:

RESOURCE TEST my_test { /* etc */ }

#define MY_TEST 1

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).


Initialising resource members

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,

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 member initialisation

simple-initialiser : number | string | resource-identifier

RESOURCE THING
 {
 string(20)="Very long string";
 }

Resource identifiers for LINKs and LLINKs

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.

See Generated resource IDs for information on how resource IDs are generated. See also NAME statement and related statements.

Initialising array items

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.

Fixed-length arrays

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.

Variable length arrays

If the array was not declared fixed-length then the number of elements is worked out from the initialiser 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 [];
 }

Using expressions to initialise array elements

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 0x04 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.

Initialising STRUCT items

struct-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-names 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:

type of entity

Punctuation needed after closing }

resource

nothing

array member (not last one)

,

last array member

nothing

struct member

;

See the definitions of resource-statement, struct-statement, struct-member and array-initialiser for a complete explanation of the syntax.


NAME statement

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 LLINKs 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-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-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.

[Top]


Compiled resource file format — .rsc

All resource files have the following format:

header

resources

index table

where:

header

is always four bytes long. It consists of two 16-bit numbers: the first gives the file offset of the start of the index table, the second gives the length in bytes of the index table

resources

are a series of variable-length data items generated in the order specified in the source file

index table

is a sequence of 16-bit numbers, the first giving the file offset of the start of the first resource, the second giving the file offset of the start of the second resource and so on up to the last resource in the file. The last 16-bit number gives the file offset of the end of the last resource (i.e. the beginning of the index table)

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......