This format has been proposed due to the multitude of different banking schemes for 2600 cartridges, along with the need to select an appropriate control device for each game. Using the .c26 format you will be able to load games without giving loads of command line switches, as you do with the current .bin system.
The extension .c26 is proposed for these files. For those on systems with upper and lower case filenames the c will be lower case, as is common with other file formats.
To avoid the format splitting into several competing ones, please do not alter the format without discussing it first. I'm not trying to be bossy, just to keep the common format truly common.
I want to keep the format as open as possible, and to be available for use in GPLed, Freeware, Shareware and Commercial 2600 emulators. Sample source code for reading and writting .c26 is available as part of Virtual 2600, and will hopefully help other emulator writers to implement the format.
Although the large number of tags defined may look daunting, the beauty of using them is that it is possible to implement a subset of them and yet still retain compatibilty with a wide range of cartridges. For example most 2 and 4k .bin's can be translated with just a DATA tag, but the format is flexible enough to allow the addition of tags for paddles, keypads, bank switching, saved games etc.
The format is tagged so as to be extensible and allow both forward and backward compatibility. It also means that information that is not needed or known does not have to be stored. e.g. If the cartridge image is not a saved game then I do not need the game state tags.
Each tag contains a tag type and the length of data in that section. If a tag is not recognised then it should be ignored. In C syntax a tag is defined as:
/* Tag structure */ struct c26_tag { int type; int len; };
with the type being set to the value of an enum.
The tag type is positive for normal tags with a following data block of length len, or negative for tags for which the length integer is sufficient to store the relevant data. This ensures that any unknown tag and its data can be skipped if encountered.
As a somewhat biased choice integers are stored as 32bit signed words in the Intel/DEC Alpha least significant byte (LSB) first system.
Also for anyone on really wierd systems, or writing an emulator in Pascal, all strings are zero terminated .
The tags are split up into sections for ease of reference. The number that each tag represents is shown in the enum statement in the following example code section.
Tags that optimizing emulators can use to get better speed/accuracy. I am a bit concerned about these as it is a possible area of incompatibilty and inconvience for users. I am looking for proposals now, but an example of the type of tag to AVOID is: SKIPFRAME(frame skipping) If I have a DX2-66 and produce an image with SKIPFRAME set to 5 it would be rather annoying to those with a P-200.
So, only include tags for things that will not vary from one user's machine to another. e.g. Combat never requires mid line collision detection.
/* The tag definitions */ enum TAGTYPE { VERSION=-1, WRITER=1, CARTNAME=2, CARTMAN=3, CARTAUTHOR=4, CARTSERIAL=5, TVTYPE=-2, CONTROLLERS=-3, BANKING=-4, DATA=6, CPUREGS=7, GAMEREGS=8, PIARAM=9, CARTRAM=10, C26MAGIC=-5, /* Added v3 */ DATACHECKSUM=-6, XSTART=-7, WIDTH=-8, YSTART=-9, HEIGHT=-10, CPUCOMP=-11 }; /* Tag structure */ struct c26_tag { int type; int len; }; /* Load from the common 2600 format */ /* Uses the tag structure from c26def.h */ /* Doesn't handle all tags yet. But the cool thing is that it doesn't matter!*/ int loadc26( FILE *fp, long flen) { char databuf[65535]; struct c26_tag tag; while(fread(&tag, sizeof(struct c26_tag), 1, fp)) { /* If positive then it has a data block */ if(tag.type>=0) { if(fread(databuf, sizeof(BYTE), tag.len, fp)) { perror("x2600 loadc26:"); fprintf(stderr, "Error reading data.\n"); } } switch(tag.type) { case VERSION: if(Verbose) printf("File version %d\n", tag.len); break; case WRITER: if(Verbose) printf("Written by: %s\n", databuf); break; case TVTYPE: base_opts.tvtype=tag.len; break; case BANKING: base_opts.bank=tag.len; break; case DATA: if(tag.len<=16384) memcpy(&theCart[0],databuf, tag.len); else { fprintf(stderr, "Data larger that 16k!\n"); exit(-1); } rom_size=tag.len; if(tag.len==2048) { memcpy(&theCart[0],databuf, tag.len); memcpy(&theCart[2048],databuf, tag.len); } break; default: fprintf(stderr, "Unknown tag %d\n. Ignoring.", tag.type); break; } } }