Various file formats used across projects.
ICN is a 1-bit graphics format.
The ICN
file contains a series of bits equivalent to pixels in a 8x8 tile. The data for each tile is made up of 64 bits, or 8 bytes, in which each bit is a pixel. An ICN is the first half of a chr file. This is the standard format for Varvara ecosystem, to view and edit icn files, try Noodle.
ch1 | hex | |||||||
---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 00 |
0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 3c |
0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 42 |
0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 7e |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 40 |
0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 42 |
0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 3c |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 00 |
Example
24x24 ICN Sprite | |
---|---|
0003 6331 397b 77f8 c0f0 f7ff fff0 8003 1c7e feff 0f07 078e f860 0c07 0300 301f 071f 7cf8 f007 7fff dcc0 c000 70f8 f8b0 0f07 4143 677f 7f3e ffe3 87cf cfcf 8703 84c4 8406 0efe fcf8 |
Implementation
Uint8 tile[8] = {0x00, 0x3c, 0x42, 0x7e, 0x40, 0x42, 0x3c, 0x00}; void draw_icn(int x, int y, Uint8 *sprite, Uint32 color) { int y2 = y + 8, h; for(; y < y2; y++, sprite++) for(h = 0; h < 8; h++) if(*sprite << h & 0x80) draw_pixel(x + h, y, color); }
CHR is a 2-bit graphics format.
The CHR
file contains a series of bits equivalent to pixels in a 8x8 tile. The data for each tile is made up of 128 bits, where the first 64 bits are the first channel, the next 64 bits the second channel, and their overlap result in a total of 4 possible colors.
ch1 + ch2 | hex | ||||||||
---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | f8 | 00 |
1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | f8 | 00 |
1 | 1 | 3 | 3 | 3 | 2 | 2 | 0 | f8 | 3e |
1 | 1 | 3 | 3 | 3 | 2 | 2 | 0 | f8 | 3e |
1 | 1 | 3 | 3 | 3 | 2 | 2 | 0 | f8 | 3e |
0 | 0 | 2 | 2 | 2 | 2 | 2 | 0 | 00 | 3e |
0 | 0 | 2 | 2 | 2 | 2 | 2 | 0 | 00 | 3e |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 00 | 00 |
This is the standard format for the Famicom and Varvara ecosystems, to view and edit chr files, try Nasu. To convert images from the tga format, use tgachr.
Uint8 tile[16] = { 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x00 }; void draw_chr(Uint32 *dst, int x, int y, Uint8 *sprite) { int v, h; for(v = 0; v < 8; v++) for(h = 0; h < 8; h++) { int ch1 = ((sprite[v] >> h) & 0x1); int ch2 = (((sprite[v + 8] >> h) & 0x1) << 1); put_pixel(dst, x + 7 - h, y + v, ch1 + ch2); } }
- ICN/CHR Toolchain, viewer and converter.
NMT is a 2-bit graphics nametable format.
The NMT
file contains a series of cells referencing addresses to sprites in a spritesheet, typically icn tiles or chr tiles. A cell is 3 bytes long, the first two bytes are the address starting from the beginning of the spritesheet, followed by a color byte. This format is used in Nasu.
Cell(3 bytes) | |
---|---|
Addr* | color |
GLY is a 1-bit inline graphic format.
Gly is an inline graphics format, similar to sixels, used to draw graphics inside text files from visible ASCII characters, in which each character represents 4 vertical pixels on 16 pixels high columns. Gly assets are supported by Left, and can be generated in Noodle.
ascii | y*4 | 4 pixels | |||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
The format resides entirely in within the 0x3f-0x7f
range of the ascii table. At the end of each row, 0x0a
denotes the end of a line. For example, a 32px high sprite, will have two line breaks. The Y position is set in increments of 4 pixels vertically for a total of 16 vertical pixels, the 0x20
advances the rendering by 1 pixel horizontally.
- 0x0a Line Feed
- 0x20 x+1
GLY Example
Each line begins with an escape code, the escape code is not display at the start of each line in the following example.
?^n~ GOb CWa ASa @Ua} NVa ?Va{ GTaq CTa{ ATa @Ta} @Ta @Ta} @Ta @Ta{ @Taq @Ta{ @Ta @Ta} @Ta @Ta @Ta @Tm~ @Td @\g @T_p @R_q @P_p HQg DSk~ BWa @O` N^n~ _w _w _w HVkw Oax GVkw @_w ?Rmx GS_w @Rkw Oax HVkw _w GVkw @Oax ?Rkw GS_w @Rmx _w _w _w N^n~ _s _q _p Og Oc Oa N^`
Implementation
Draw a gly string in Uxntal with the screen's auto byte is set to auto-y(0x02)
:
@draw-gly ( chr -- ) ( cmds ) LDAk #20 NEQ ?&no-x .Screen/x DEI2k INC2 ROT DEO2 INC2 !&w &no-x LDAk #20 LTH ?&end ( opcode ) LDAk LIT "? SUB STH .Screen/y DEI2 #00 STHkr #30 AND #24 SFT OVR2 ADD2 .Screen/y DEO2 #0400 &loop STHkr OVR SFT #01 AND .Screen/pixel DEO INC GTHk ?&loop POP2 POPr .Screen/y DEO2 INC2 !&w JMP2r
UFX is a proportional font format.
The UFX
file begins with 256 bytes corresponding to the width(in pixels) of each of the 256 glyphs in the spritesheet, followed by the pixel data in the .icn format for each character.
Extension | Size(px) | Filesize |
---|---|---|
.uf1 | 8x8(1 tile) | 0x0900 |
.uf2 | 16x16(4 tiles) | 0x2100 |
.uf3 | 24x24(9 tiles) | 0x4900 |
The pixel data for each glyph is stored in a series of 8x8 tiles, the drawing order goes vertically as to be able to skip extra draw calls for narrow characters if needed:
0 | 2 |
1 | 3 |
A naive uf2 character drawing routine in Uxntal is about 50 bytes, with the screen's auto byte set to #15
:
@draw-uf2 ( text* -- ) [ LIT2 15 -Screen/auto ] DEO &>while ( -- ) LDAk #20 SUB #00 SWP ( addr ) DUP2 #50 SFT2 ;font/glyphs ADD2 .Screen/addr DEO2 ( move ) ;font ADD2 LDA #00 SWP .Screen/x DEI2 ADD2 ( draw ) [ LIT2 01 -Screen/sprite ] DEOk DEO .Screen/x DEO2 INC2 LDAk ?&>while POP2 JMP2r
The empty pixel data of the first 32 invisible characters are typically removed. You will find this filetype in the Uxn ecosystem, namely in Left. Uf2 fonts can be viewed and edited with Turye.
ULZ is a compression format.
This LZ lossless compression format is designed to be mildly better than RLE but not too difficult to host on Uxn systems. The compressed file contains a stream of commands, not unlike a virtual machine bytecode. There are two types of instructions LIT and CPY, the CPY opcode has a short and a longer mode. Decoding works by reading the commands from the input until there's no more input.
Byte | Byte | Byte | ||
---|---|---|---|---|
0 | LIT(length, 7 bits) | Bytes to copy at pointer... | ||
1 | 0 | CPY1(length, 6 bits) | Offset from pointer | |
1 | 1 | CPY2(length, 14 bits) | Offset from pointer |
As the output file is being assembled, a pointer moves along, and the program appends previously written data at the pointer's position up to a maximum of 256 bytes ago. When the writing length overflows the distance from the output pointer, the bytes loop over the available length.
Encoded Data
2842 6c75 6520 6c69 6b65 206d 7920 636f 7276 6574 7465 2069 7473 2069 6e20 616e 6420 6f75 7473 6964 650a 8128 2361 7265 2074 6865 2077 6f72 6473 2049 2073 6179 0a41 6e64 2077 6861 7420 4920 7468 696e 6b8a 2909 6665 656c 696e 6773 0a54 8022 066c 6976 6520 696e 8050 1720 6d65 0a49 276d 2062 6c75 650a 4461 2062 6120 6465 6520 6482 0900 69b5 12
The LIT Instruction
The LIT instruction appends a number of bytes to the output equal to the 7 lower bits of the instruction byte, plus 1. The output pointer is moved by that same distance.
Blue like my corvette its in and outside are the words I say And what I thinkfeelings Tlive in me I'm blue Da ba dee di
The CPY Instruction
The CPY instruction copies a length of bytes, plus 4, at a negative offset from the output pointer, plus 1. In other words, an offset of 0 means go back by 1 bytes into the history. The offsets should be treated as the distance from the end of last byte that was written.
Blue like my corvette its in and outside -----are the words I say And what I think--------------feelings T----live in---- me I'm blue Da ba dee d------i--------------------------------------------------------
The resulting 209 bytes of data from the 137 bytes of compressed data. Note that this short example is not long enough to include usage of the CPY2 instruction.
Blue like my corvette its in and outside Blue are the words I say And what I think Blue are the feelings That live inside me I'm blue Da ba dee da ba di Da ba dee da ba di Da ba dee da ba di Da ba dee da ba di
Image Compression
The compression works best with tiled assets in the icn or chr formats.
Original: 4096 bytes Compressed: 2430 bytes, 59.32% |
Implementation
Here's an implementation in Uxntal.
@decode_ulz ( str* -- ) ;mem .ptr STZ2 .File/name DEO2 &stream ( -- ) #0001 .File/length DEO2 ;&b DUP2 .File/read DEO2 .File/success DEI2 ORA ?{ POP2 JMP2r } [ LIT &b $1 ] decode_ulz_byte !&stream @decode_ulz_byte ( byte -- ) DUP #80 AND ?op-cpy @op-lit ( byte -- ) #00 SWP INC2 DUP2 .File/length DEO2 .ptr LDZ2 DUP2 .File/read DEO2 ADD2 .ptr STZ2 JMP2r @op-cpy ( byte -- ) #7f AND DUP #40 AND ?&long #00 SWP !© &long ( byte -- ) #3f AND getc © ( length* -- ) .ptr LDZ2 #00 getc INC2 SUB2 STH2 #0004 ADD2 #0000 &l ( -- ) ( get ) DUP2 STH2kr ADD2 LDA ( put ) .ptr LDZ2 STAk INC2 .ptr STZ2 POP INC2 GTH2k ?&l POP2 POP2 POP2r JMP2r
And an implementation in C89.
char *mem, *ptr; int decode_ulz(FILE *src) { char c, *copy; short i, length; ptr = mem = malloc(0x10000); while((c = getc(src)) != EOF) { if(c & 0x80) { /* CPY */ if(c & 0x40) length = (c & 0x3f) << 8 | getc(src); else length = c & 0x3f; copy = ptr - (getc(src) + 1); for(i = 0; i < length + 4; i++) *(ptr++) = *(copy++); } else /* LIT */ for(i = 0; i < c + 1; i++) *(ptr++) = getc(src); } return ptr - mem; }
Truevision TGA is a raster graphics file format created by Truevision.
TGA files are currently used as the standard image transfer format between Varvara and the host operating system.
Length | Field name | Description |
---|---|---|
1 byte | ID length | Length of the image ID field |
1 byte | Color map type | Whether a color map is included |
1 byte | Image type | Compression and color types |
5 bytes | Color map specification | Describes the color map |
Specification | ||
2 bytes | X-origin | absolute x of lower-left corner |
2 bytes | Y-origin | absolute y of lower-left corner |
2 bytes | Image width | width in pixels |
2 bytes | Image height | height in pixels |
1 byte | Pixel depth | bits per pixel |
1 byte | Image descriptor | bits 3-0 give the alpha channel depth, bits 5-4 give direction |
Image ID length
0–255 The number of bytes that the image ID field consists of. The image ID field can contain any information, but it is common for it to contain the date and time the image was created or a serial number.
Color map type
- 0 if image file contains no color map
- 1 if present
- 2–127 reserved by Truevision
- 128–255 available for developer use
Image type
Enumerated in the lower three bits, with the fourth bit as a flag for RLE. Some possible values are:
- 0 no image data is present
- 1 uncompressed color-mapped image
- 2 uncompressed true-color image
- 3 uncompressed black-and-white (grayscale) image
- 9 run-length encoded color-mapped image
- 10 run-length encoded true-color image
- 11 run-length encoded black-and-white (grayscale) image
Image type 1 and 9: Depending on the Pixel Depth value, image data representation is an 8, 15, or 16 bit index into a color map that defines the color of the pixel. Image type 2 and 10: The image data is a direct representation of the pixel color. For a Pixel Depth of 15 and 16 bit, each pixel is stored with 5 bits per color. If the pixel depth is 16 bits, the topmost bit is reserved for transparency. For a pixel depth of 24 bits, each pixel is stored with 8 bits per color. A 32-bit pixel depth defines an additional 8-bit alpha channel. Image type 3 and 11: The image data is a direct representation of grayscale data. The pixel depth is 8 bits for images of this type.
Color map specification
- First entry index (2 bytes): index of first color map entry that is included in the file
- Color map length (2 bytes): number of entries of the color map that are included in the file
- Color map entry size (1 byte): number of bits per pixel
In case that not the entire color map is actually used by the image, a non-zero first entry index allows to store only a required part of the color map in the file.
A tree representation typically represented with parentheses.
When representing source code in Lisp, the first element of an S-expression is commonly an operator or function name and any remaining elements are treated as arguments.
- Lists and pairs:
(1 () (2 . 3) (4))
- Symbols:
with-hyphen
?@!$
|a symbol with spaces|
- Strings:
"Hello, world!"
- Integers:
-9876543210
- Floating-point numbers:
-0.0
6.28318
6.022e23
Example
(defun factorial (x) (if (zerop x) 1 (* x (factorial (- x 1)))))
A tree representation typically represented with indentations.
The syntax uses indentation to group expressions, and has no special cases for semantic constructs of the language. It can be used both for program and data input. It also allows mixing with S-expressions freely, giving the programmer the ability to layout the code as to maximize readability.
The s-exp (* (+ 1 2) (/ 3 4))
, can be represented as the following i-exp:
* + 1 2 / 3 4
Example
define fac x if = x 0 1 * x fac - x 1