Fxi is the format used by the popular software Fx-Interface, very used in Europe. Fxi is a binary format, and can contain any kind of data excepted backups. Fxi files are ended by .fxi
This format is very complex, its access is done in two step.
We must first convert each byte of the fxi file by its equivalent in the raw format. A tutorial of Alexis Soulard, le format FXI (in french), explain the ugly algorithm to build an equivalence table (thank you very much Alexis!). Here is the algorithm of Alexis in php:
<table border="1" width="120" cellpadding="2"> <tr> <td width="50%" align="center" bgcolor="#F0F0F0"><b>ASCII</b></td> <td width="50%" align="center" bgcolor="#F0F0F0"><b>FXI</b></td> </tr> <?php //au début l'équivalence 255 (DEC) est 24 en FXI $num = 24; //chaine maximale de nombres qui se suivent $chaineMaxi = 8; //nombre de cycle à suivre avant changements $nbMaxiCycles = 4; $compteNum = 1; $nbCycle = 1; $numDebCycle = $num; $chaineAllValues = ""; $chaineAllValues2 = ""; for ($i=255; $i>=0; $i--){ echo "<tr>n"; echo "<td width="50%" align="right" bgcolor="#F0F0F0"><b>".$i."</b></td>n"; echo "<td width="50%" align="right">".$num."</td>n"; $chaineAllValues .= $i."=>".$num.","; $chaineAllValues2 .= $num."=>".$i.","; $num++; $compteNum++; if ($compteNum > $chaineMaxi){ $compteNum = 1; $num -= $chaineMaxi * 2; $nbCycle++; } if ($nbCycle > $nbMaxiCycles){ $num = $numDebCycle + $chaineMaxi * $nbMaxiCycles; $numDebCycle = $num; $nbCycle = 1; } echo "</tr>n"; } ?> </table> <?php echo "<br><br>$tabFxiDec = array("; echo $chaineAllValues; echo ");"; echo "<br><br>$tabDecFxi = array("; echo $chaineAllValues2; echo ");"; ?>
And here is the same algorithm in python:
#Build the raw2fxi dic #Thanks to http://www.casioland.net/tutoriaux.php?idTuto=3&noHeader=1 r2f = {} dep, a, i, j = 23, 255, 0, 0 v = dep while a >= 0 : v = v + 1 r2f[a] = v a -= 1 i += 1 if i == 8 : dep -= 8 j += 1 i = 0 v = dep if j == 4 : j = 0 dep += 8 * 8 v = dep #Build the fxi2raw dic f2r = {} for a, f in r2f.items(): f2r[f] = a # Function to convert fxi data in raw data: def fxiDecode(fxiData): """ Convert fxi-like data to raw data """ out = '' for c in range(len(fxiData)) : out += chr(f2r[ord(fxiData[c])]) return out
As many formats, the fxi formats can contain many record in a single file, and some metadata.
The more easier way to extract programs from a fxi file is to use a regular expression (here in python with the re module, and using the fxi_decode function from the last example):
fxi_file = open(filename, 'r') # Get the raw data ctn = fxi_decode(fxi_file.read()) fxi_file.close() # Make the regular expression data_pattern = re.compile(''' (TXT) # data format (but fxi txt is not really text :-/) . # separator (PG) # type of data .{4} # 4 characters ([^\xff]{1,8}) # data name \xff{16,23} # between 16 and 23 (16+8-len(data name)) 0xFF (NL|BN) # Option1 (Normal or Base) \xff{12} # 12 0xFF ([^\xff]+\xff) # any non 0xFF characters ended by one 0xFF : raw data ''', re.VERBOSE) # Extract records from raw data using the regular expression data_list = data_pattern.findall(ctn) for record in data_list: format, data_type, name, optn, data = record # You can use data_type to know the record type, name for the program name. # optn == "BN" for a base program, optn == "NL" instead, and data contain # raw data of the program.
The more easier way to extract pictures from a fxi file is to use a regular expression (here in python with the re module, and using the fxi_decode function from the last example):
fxi_file = open(filename, 'r') # Get the raw data ctn = fxi_decode(fxi_file.read()) fxi_file.close() # Make the regular expression pict_pattern = re.compile(''' (IMG) # data format . # separator (PC) # type of data .{4} # 4 characters ([^\xff]+) # data name (old:([^\xff]{1,8})) \xff{1,23} # between 8 and 23 (16+8-len(data name)) 0xFF (o:{16,23}) (DRUWF) # Sens of reading .{4} # 4 bytes \xff{13} # Separator (.{4112}) # (( sheet of 16 * 64 byte ) + 4 header byte) #* 4 colors ''', re.VERBOSE) # Extract records from raw data using the regular expression data_list = pict_pattern.findall(ctn) for record in data_list: format, data_type, name, optn, data = record # You can use data_type to know the record type, name for the picture name. # data contain raw data of the picture.
The picture raw data is in the casio picture format, with the pallet [orange, blue, green, white], the colour byte is 3.
A fxi file begin by a file header, followed by data records.
The file header is composed as follow, with static data. Here it's done as python code, strings like \x2a mean a byte with the hexadecimal value 2a.
file_header = '\xd5\xd7\x1fFX-INTERFACE - YELLOW COMPUTING' file_header += '\x00'*40 + '\x06' + '\x00'*81 + '\x07\x00' file_header += '\xff'*2 + '\x00'*2 + '\x0f\x00CYCFXListItemNP' + '\x00'*8 file_header += '\x03\x00\x15\x18' + '\x00'*5 + '\x4a\xa0' + '\x00'*76 file_header += '\x01\x80\x00\x00\x01' + '\x00'*3 + '\x01\x00\x03\x00\x55\x1a' file_header += '\x00'*2 + '\x04' + '\x00'*2 + '\x5b\x80' + '\x00'*76 + '\x01\x80' file_header += '\x00'*2 + '\x02' + '\x00'*3 + '\x02' + '\x00'*3 + '\x08\x13' file_header += '\x00'*2 + '\x22\x00\x00\x83\x80' + '\x00'*76 + '\x01\x80' file_header += '\x00'*2 + '\x03' + '\x00'*3 + '\x01\x00\x03\x00\x55\x1a\x00\x00' file_header += '\x20' + '\x00'*2 + '\x1f\x81' + '\x00'*76 + '\x01\x80' + '\x00'*2 file_header += '\x04' + '\x00'*3 + '\x02' + '\x00'*3 + '\x08\x13' + '\x00'*2 file_header += '\x21' + '\x00'*2 + '\x83\x80' + '\x00'*76 + '\x01\x80' + '\x00'*2 file_header += '\x05' + '\x00'*5 + '\x02\x00\x58\x10' + '\x00'*2 + '\x1e' file_header += '\x00'*2 + '\xcc\x80' + '\x00'*76 + '\x01\x80' + '\x00'*2 + '\x06' file_header += '\x00'*3 + '\x01' + '\x00'*3 + '\x08\x11' + '\x00'*2 + '\x1e' file_header += '\x00'*2 + '\x83\x80' + '\x00'*76
After this header, the file is sorted by data type block, in the right order: first the program block, next the picture block.
If there is no program to save, and no data in a next block, the program block is empty. Else, if there is no program to save, but a non empty block after, the program block is '\x00\x00'.
If the program block is not empty, it begin by two byte: the first has for value the number of program records, and the second is null ('\x00'). After this two byte, each program is in one record.
A program record is composed by a record header, and the program itself. The record is composed as:
A fxi file made by Fx-Interface may also contain at the end of the record, inside some “\xff” bytes, some data you have deleted from your program. Saved deleted data in the file is fully optional, if you want write a fxi file, you can forgot this.
Warning: if the first program length must be coded on more than 2 bytes, the organization of the record change. This is not documented for the moment.
If there is no picture to save, the picture block is empty.
If the picture block is not empty, it begin by:
A picture record is composed by a record header, and the picture itself. The record is composed as:
The file is ended by 6 or7 “\x00” bytes.
Using the work of Alexis Soulard (see before), we just to the opposite. Here is a python function to encode in fxi format:
# Build the raw2fxi dic # Thanks to http://www.casioland.net/tutoriaux.php?idTuto=3&noHeader=1 r2f = {} dep, chrNumber, fxi_index, fxi_index2 = 23, 255, 0, 0 chr_value = dep while chrNumber >= 0: chr_value = chr_value + 1 r2f[chrNumber] = chr_value chrNumber -= 1 fxi_index += 1 if fxi_index == 8: dep -= 8 fxi_index2 += 1 fxi_index = 0 chr_value = dep if fxi_index2 == 4: fxi_index2 = 0 dep += 8 * 8 chr_value = dep # Encode function def fxi_encode(raw_data): """Convert raw data to fxi-like data""" out = '' for car in range(len(raw_data)): out += chr(r2f[ord(raw_data[car])]) return out # If your file content is in the string variable 'content', you can save your file by: myfile = open('path/to/my.fxi', 'w') myfile.write(fxi_encode(content)) myfile.close()