RIC file format

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
Post Reply
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

RIC file format

Post by mattallen37 »

I've been searching for a while, but I can't seem to find information about the structure of RIC files. Can someone please explain, or point me to an explanation?
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
muntoo
Posts: 834
Joined: 01 Oct 2010, 02:54
Location: Your Worst Nightmare
Contact:

Re: RIC file format

Post by muntoo »

I don't know much about RICs either, but this post explains some.
NXTasy Blog wrote:RIC Revealed

NXT graphic files are often used to spice up a robotics program. The Alpha Rex robot uses two heart pictures to display something like a beating heart on the LCD while the robot moves about. Ross Crawford’s mini chess program written in NBC uses several graphics to draw the chess pieces on the screen. The LEGO MINDSTORMS NXT software ships with nearly one hundred little graphic images that you can use in NXT-G programs or programs written in NBC or NXC. Read on for a revealing look into the NXT graphic file format.

These neat little files are known as pictures or graphics. They have a .ric file extension. In the NXT world files typically end with a .r?? extension where the last two characters in the file extension indicate the type of file. Sound files have a .rso extension. Executable files which have a .exe extension in Dos or Windows have a .rxe extension on the NXT. Try Me programs (which are also executables) have a .rtm file extension while NXT Programs have a .rpg file extension. The “ic” in the RIC file extension indicates that it is a pICture or graphIC.

With the LEGO MINDSTORMS NXT software you can browse through the list of pictures that ship with the program whenever you wire a Display block into your NXT-G program. As you highlight a picture name in the list the preview window shows you the image contents of each file. This may give you the impression that an NXT picture file is kind of like a bitmap file - that the RIC file format is some form of an image file format akin to .bmp or .gif or maybe .jpg. But that could not be further from the truth. An RIC file is actually very different from your usual image file format.

An RIC actually contains a script or program of sorts which consists of a list of drawing commands or opcodes. These commands are executed whenever you use the Display block in NXT, call the GraphicOut API function in NBC/NXC, or directly execute the low level syscall opcode in NBC with the DrawGraphic system call function identifier along with a TDrawGraphic structure. They are executed in the order that they occur within the RIC file as it is read by the NXT firmware in response to the DrawGraphic system call. I suppose because RIC files are akin to executable files, like other executable files on the NXT and unlike data files or sound and melody files, the NXT firmware requires that RIC files are linear files, which means that they are not allowed to be fragmented across multiple sectors in the NXT filesystem.

Another important point to be aware of, regarding not only RIC files but all types of files on the NXT, is that every file on the NXT takes up one or more chunks of file system (flash) memory and each chunk is 256 bytes in size. You may have an RIC file that is quite small, perhaps only 40 bytes or so, but it will still use up one 256 byte chunk of flash memory. This means that when you create an RIC file you may want to optimize it so that its size doesn’t go slightly over another 256 byte boundary. Keep this in mind for sound files and melody files as well.

So what are the opcodes or commands that you can tell the NXT firmware to execute within an RIC file? Here’s the list:

Description
Sprite
VarMap
CopyBits
Pixel
Line
Rectangle
Circle
NumBox

That’s nine separate commands or operations that you can execute within an RIC file. Each of these can occur multiple times within a single RIC file. When you draw an RIC file you are sequentially executing one or more of the commands listed here. Each command is defined by its ID or opcode value, 0 through 8, followed by its arguments. In a C or C++ program each opcode in an RIC file takes the form of a structure with various member fields that define the arguments of the opcode.

In order to make reading the opcodes from an RIC file simple, each opcode in an RIC file begins with an unsigned word value (2 bytes) that indicates how many more bytes in the RIC file are used by the current opcode. This is the size of the opcode structure minus 2 (since two bytes for the size have already been read). Each opcode follows the size word with the opcode ID which is also an unsigned word value.

The smallest opcode is the Description opcode. The structure size, in total, is 10 bytes. That means that in an RIC file if you add a Description opcode to the file the opcode will start with 0×08 0×00. This is “little-endian” since the least significant byte (the low-order byte) is written first. As mentioned, the value of size is 2 less than the actual size of the opcode since it is there to tell the parser how many more bytes it needs to read before it gets to the end of the current opcode. Following the size is the Description opcode’s ID, which is 0 (zero). So there will be two bytes (an unsigned word written in little-endian format) of zero after the size to tell the NXT it is to execute a Description opcode.

The Description opcode has 3 word-sized arguments. The first is Options. It is meaningless in the current firmware. The second argument is the Width and the third argument is the Height. Both are unsigned words (2 bytes each). They “describe” the total width and height of the RIC file in its entirety. If you use a Description command in an RIC file it should correctly specify the drawing extents of every other drawing command within the RIC. However, the Description opcode is actually a no-op when the RIC is executed by the NXT firmware. It ignores all the values within this opcode so they can be whatever you want. Leave out the Description opcode altogether if you want and save 10 bytes. The Description structure is shown below.

typedef struct
{
UWORD OpSize;
UWORD OpCode;
UWORD Options;
UWORD Width;
UWORD Height;
} IMG_OP_DESCRIPTION;

The one caveat that needs to be mentioned is that the LEGO MINDSTORMS NXT Software prefers that an RIC file starts with a Description opcode. It uses the information in the Description opcode to help it correctly draw the RIC file in the Display block preview window. An RIC file that does not start with a Description opcode may not always draw correctly within the NXT software preview window but it will draw correctly when your program runs on the NXT.

The most important opcode from the LEGO MINDSTORMS NXT Software perspective and one of three opcodes that are used exclusively by the RIC files that come with the NXT software is the Sprite opcode. A Sprite opcode is basically a bitmap. Its arguments define the number of rows and columns of pixels are to be set or cleared. Here is the Sprite structure:

typedef struct
{
UWORD OpSize;
UWORD OpCode;
UWORD DataAddr;
UWORD Rows;
UWORD RowBytes;
UBYTE Bytes[2];
} IMG_OP_SPRITE;

As you can see the size of this opcode is variable. It depends on how many pixels are defined within the Sprite. The pixel data in the Sprite is laid out by rows starting with the top row of the Sprite and continuing with each row below it in sequence. Each byte in the Bytes array represents 8 horizontal pixels in a single row. So if your RIC is 24 pixels wide then the RowBytes value will be 3. If your RIC is anywhere from 25 pixels wide to 32 pixels wide then the RowBytes will be 4 (since each byte accounts for 8 pixels). The height of your RIC is the Row value in the Sprite. If you examine an RIC file whose width is 24 pixels then Bytes[0] through Bytes[2] will represent the top row of Sprite data, Bytes[3] through Bytes[5] will represent the second row, and so on until all the Rows in the Sprite are defined by data in the Bytes array.

The total size of the Bytes array is Rows*RowBytes. The size of the Bytes array must be even, however, so if Rows*RowBytes is odd the Bytes array size will be Rows*RowsBytes+1 where the extra byte is equal to zero. The reason why the Bytes array size must be even is because the NXT firmware running on the ARM processor wants all structures to be word-aligned. It does not like structures to have an odd number of bytes.

If your RIC has a height of one the Rows value will be one. If your RIC has a width of 1 the RowBytes value will be one and the Bytes array will have only two bytes in it (one byte for the single pixel and one pad byte). In that case the total size of the Sprite opcode would be 12 bytes and the value of OpSize would be 10. If the byte has all 8 bits (pixels) set then the corresponding bytes of data in the RIC file for the Sprite opcode would be like this:

0A 00 01 00 01 00 01 00 01 00 FF 00

The DataAddr argument to the Sprite opcode is very important. This address is an index into a global array of structure pointers. The array can store 10 entries numbered 1 through 10. Each Sprite in an RIC file is stored at the index specified by this argument. When the Sprite opcode is executed by the NXT firmware the only thing that occurs is that a pointer to the Sprite structure is copied into the data array at the specified index. No image data is drawn to the NXT LCD at this point. What this means is if you reuse the same data address value in subsequent Sprite opcodes or the VarMap opcodes then any Sprite or VarMap data previously written to that address will be no longer useable. All the RIC files that ship with the NXT set use a single Sprite opcode and they all use a data address value of one.

The CopyBits opcode is the third opcode used exclusively by the RIC files that come with the LEGO MINDSTORMS NXT software. Each of these RIC files begins with a Description, followed by a Sprite, and concluding with a CopyBits opcode. The Display block’s preview window can only draw RIC files that use a Sprite and a CopyBits opcode. It does not know how to preview RIC files that have multiple Sprites and CopyBits opcodes or RIC files that use other opcodes supported by the standard NXT firmware.

The CopyBits opcode ID is 3. Its size is fixed at 20 bytes. That means the OpSize value is 18 (0×12 hex). This opcode is defined using three structures as you see below.

typedef struct
{
SWORD X, Y;
} IMG_PT;

typedef struct
{
IMG_PT Pt;
SWORD Width, Height;
} IMG_RECT;

typedef struct
{
UWORD OpSize;
UWORD OpCode;
UWORD CopyOptions;
UWORD DataAddr;
IMG_RECT Src;
IMG_PT Dst;
} IMG_OP_COPYBITS;

The CopyOptions argument of the CopyBits opcode sounds very promising. It can be set to a value that was intended to mean Copy, CopyNot, Or, and BitClear (presumably 0 through 3). Unfortunately, the CopyOptions argument found in this opcode and other opcodes is unused in the existing standard NXT firmware. All the opcodes with the CopyOptions argument operate as if the argument was set to Copy (zero).

The CopyBits opcode is designed to copy bits from a previously loaded Sprite into the normal LCD display memory. Which Sprite data is copied is determined by the DataAddr argument to the CopyBits opcode. If you want to copy bits from the Sprite loaded into slot 1 then you need to set the same data address in your CopyBits opcode. The bits to copy from the Sprite are specified by the source rectangle (left, bottom, width, height). The CopyBits opcode also specifies the destination point (left, bottom) where the copied bits of Sprite data should be drawn. This x, y location is usually set to 0, 0 but it can be used as an offset from the drawing origin specified in the DrawGraphic system call.

If you want to copy all the bits in the Sprite to the NXT LCD then the source rectangle values will be 0, 0, RowBytes*8, Rows However, since you may have extra bits at the end of each row as a result of each byte in a row representing 8 pixels you probably will want to use the actual desired RIC width rather than RowBytes*8.

You can also use the CopyBits opcode to only draw a portion of the Sprite data on the screen by manipulating the source rectangle values. You could use a single Sprite opcode with multiple CopyBits opcodes which select different portions of the Sprite data and draw them to the screen or multiple CopyBits opcodes which select the same portion of Sprite data and draw it to different relative screen destinations.

So now we have discussed the big three opcodes (one of which is unused completely when drawing on the NXT LCD). That leaves us with the unused opcodes, the defined but unusable opcodes, and the mysterious VarMap opcode. The remaining opcodes will be further revealed in a separate post.
And then there's a whole bunch of information under Andreas Dreier's page.

I bet there is some info in the Official LEGO docs somewhere...
Image

Commit to LEGO Mindstorms Robotics Stack Exchange:
bit.ly/MindstormsSE


Commit to LEGO Stack Exchange: bit.ly/Area51LEGOcommit
spillerrec
Posts: 358
Joined: 01 Oct 2010, 06:37
Location: Denmark
Contact:

Re: RIC file format

Post by spillerrec »

muntoo wrote:... I bet there is some info in the Official LEGO docs somewhere...
I haven't been able to find anything there, those two posts on NXTasy, while incomplete and a bit outdated now, are probably the best resources right now.
You can also use the RIC macros in NXC if you want to know exactly how it is encoded, the definition of them is in the API iirc.

From developing RICcreator I know quite a bit about the format, so if there is anything specific you want to know/have clarified after reading that post, just ask.
My blog: http://spillerrec.dk/category/lego/
RICcreator, an alternative to nxtRICeditV2: http://riccreator.sourceforge.net/
Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests