summaryrefslogtreecommitdiffstatshomepage
path: root/src/mess/tools/imgtool/modules/psion.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mess/tools/imgtool/modules/psion.c')
-rw-r--r--src/mess/tools/imgtool/modules/psion.c684
1 files changed, 684 insertions, 0 deletions
diff --git a/src/mess/tools/imgtool/modules/psion.c b/src/mess/tools/imgtool/modules/psion.c
new file mode 100644
index 00000000000..f4b8566612e
--- /dev/null
+++ b/src/mess/tools/imgtool/modules/psion.c
@@ -0,0 +1,684 @@
+/***************************************************************************
+
+ Psion Organiser II Datapack
+
+ 08/18/2010 Sandro Ronco
+
+ Known file types:
+ 0x00 invalid record
+ 0x01 - 0x7e deleted record
+ 0x7f invalid deleted record
+ 0x80 long record
+ 0x81 file name
+ 0x82 diary
+ 0x83 OPL/OB3 procedure
+ 0x84 RS232 setup
+ 0x85 - 0x8f reserved
+ 0x90 MAIN Record
+ 0x91 - 0xfe data records from files
+ 0xff invalid record
+
+****************************************************************************/
+
+#include "imgtool.h"
+
+#define MAXFILES 256
+
+struct psion_file
+{
+ char filename[9];
+ UINT8 type;
+ UINT8 id;
+
+ UINT16 name_rec;
+ UINT16 data_rec;
+};
+
+struct psion_pack
+{
+ imgtool_stream *stream;
+
+ UINT16 eop;
+ struct psion_file pack_index[MAXFILES];
+};
+
+struct psion_iter
+{
+ UINT16 index;
+};
+
+UINT16 head_checksum(UINT8* data)
+{
+ UINT16 checksum = 0;
+
+ for (int i=0; i<6; i+=2)
+ checksum += (data[i]<<8 | data[i+1]);
+
+ return checksum;
+}
+
+UINT16 get_long_rec_size(imgtool_stream *stream)
+{
+ UINT8 size_h, size_l;
+
+ stream_read(stream, &size_h, 1);
+ stream_read(stream, &size_l, 1);
+
+ return (size_h<<8) | size_l;
+}
+
+UINT32 update_pack_index(psion_pack *pack)
+{
+ UINT8 data, type;
+ UINT16 size;
+ UINT16 index = 0;
+
+ memset(pack->pack_index, 0, sizeof(psion_file) * MAXFILES);
+
+ // start at the first record
+ stream_seek(pack->stream, 0x10, SEEK_SET);
+
+ do
+ {
+ stream_read(pack->stream, &data, 1);
+
+ if(data == 0xff)
+ {
+ pack->eop = stream_tell(pack->stream) - 1;
+ return TRUE;
+ }
+ else if (data == 0x02)
+ {
+ // long record without name are ignored
+ stream_read(pack->stream, &data, 1);
+ size = get_long_rec_size(pack->stream);
+ stream_seek(pack->stream, size, SEEK_CUR);
+ }
+ else
+ {
+ stream_read(pack->stream, &type, 1);
+
+ // deleted record are not listed
+ if (type < 0x90 && (type & 0x80))
+ {
+ pack->pack_index[index].type = type;
+ stream_read(pack->stream, &pack->pack_index[index].filename, 8);
+ stream_read(pack->stream, &pack->pack_index[index].id, 1);
+ pack->pack_index[index].name_rec = stream_tell(pack->stream) - 11;
+
+ //check for data record
+ stream_read(pack->stream, &data, 1);
+ if (data == 0x02)
+ pack->pack_index[index].data_rec = stream_tell(pack->stream) - 1;
+
+ stream_seek(pack->stream, -1, SEEK_CUR);
+
+ index++;
+ }
+ else
+ stream_seek(pack->stream, data, SEEK_CUR);
+ }
+
+ } while (stream_size(pack->stream) > stream_tell(pack->stream));
+
+ // corrupted image
+ return FALSE;
+}
+
+int seek_next_record(imgtool_stream *stream, UINT8 id)
+{
+ UINT8 data, rec_id;
+ UINT16 size;
+
+ do
+ {
+ stream_read(stream, &data, 1);
+
+ if(data == 0xff)
+ break;
+
+ if (data == 2)
+ {
+ stream_read(stream, &rec_id, 1);
+ size = get_long_rec_size(stream);
+
+ }
+ else
+ {
+ stream_read(stream, &rec_id, 1);
+ if (id == rec_id)
+ {
+ stream_seek(stream, -2, SEEK_CUR);
+ return TRUE;
+ }
+ size = data;
+ }
+
+ // next record
+ stream_seek(stream, size, SEEK_CUR);
+
+ } while (stream_size(stream) > stream_tell(stream));
+
+ return FALSE;
+}
+
+// if there are multiple files with the same name, only the first is found
+int seek_file_name(psion_pack *pack, const char *filename)
+{
+ UINT16 index = 0;
+
+ while (pack->pack_index[index].name_rec)
+ {
+ if (!strncmp(filename, pack->pack_index[index].filename, strlen(filename)))
+ return index;
+ index++;
+ }
+
+ // filename not found
+ return -1;
+}
+
+UINT8 get_free_file_id(psion_pack *pack)
+{
+ for (UINT8 file_id=0x91; file_id<0xff; file_id++)
+ {
+ int index = 0;
+
+ while (pack->pack_index[index].id != file_id)
+ if (pack->pack_index[index++].name_rec == 0)
+ return file_id;
+ }
+
+ return 0xff;
+}
+
+void put_name_record(imgtool_stream *stream, const char* filename, UINT8 record_type, UINT8 record_id)
+{
+ char data[0x10];
+ int i = 0;
+
+ data[i++] = 0x09;
+ data[i++] = record_type;
+
+ // filename is 8 char long space filled
+ for (int j=0; j<8; j++)
+ if (j < strlen(filename))
+ data[i++] = filename[j];
+ else
+ data[i++] = 0x20;
+
+ data[i++] = record_id;
+
+ stream_write(stream, data, i);
+}
+
+void update_opk_head(imgtool_stream *stream)
+{
+ UINT16 size = stream_size(stream) - 6;
+
+ stream_seek(stream, 4, SEEK_SET);
+ stream_putc(stream, (size>>8) & 0xff);
+ stream_putc(stream, size & 0xff);
+}
+
+char *stream_getline(imgtool_stream *source, UINT16 max_len)
+{
+ UINT16 pos = 0;
+ char data;
+ char *line = (char*)malloc(max_len);
+ memset(line, 0, max_len);
+
+ while (pos < max_len && stream_size(source) > stream_tell(source))
+ {
+ stream_read(source, &data, 1);
+
+ switch(data)
+ {
+ case '\r':
+ stream_read(source, &data, 1);
+ if (data != '\n')
+ stream_seek(source, -1, SEEK_CUR);
+ case '\n':
+ return line;
+ break;
+ default:
+ line[pos++] = data;
+ break;
+ }
+ }
+
+ if (pos)
+ return line;
+
+ free(line);
+ return NULL;
+}
+
+UINT16 put_odb(imgtool_stream *instream, imgtool_stream *outstream, UINT8 file_id)
+{
+ char *line;
+ UINT16 out_size = 0;
+
+ // reset stream
+ stream_seek(instream, 0, SEEK_SET);
+
+ while ((line = stream_getline(instream, 256)))
+ {
+ UINT16 len = strlen(line);
+
+ stream_putc(outstream, (UINT8)len);
+ stream_putc(outstream, file_id);
+ stream_write(outstream, line, len);
+
+ out_size += (len + 1);
+
+ free(line);
+ }
+
+ // end of pack
+ stream_fill(outstream, 0xff, 2);
+
+ return out_size + 4;
+}
+
+UINT16 put_ob3(imgtool_stream *instream, imgtool_stream *outstream)
+{
+ UINT16 size = stream_size(instream) - 6;
+ UINT8 *buffer = (UINT8*)malloc(size);
+
+ stream_seek(instream, 6, SEEK_SET);
+ stream_read(instream, buffer, size);
+
+ stream_write(outstream, buffer, size);
+
+ free(buffer);
+
+ // end of pack
+ stream_fill(outstream, 0xff, 2);
+
+ return size;
+}
+
+UINT16 put_opl(imgtool_stream *instream, imgtool_stream *outstream)
+{
+ UINT16 out_size = 0;
+ UINT32 rec_start = stream_tell(outstream);
+ char *line;
+
+ // reset stream
+ stream_seek(instream, 0, SEEK_SET);
+
+ stream_fill(outstream, 0x00, 4);
+
+ // replace all eol with 0x00
+ while ((line = stream_getline(instream, 256)))
+ {
+ // replace tab with space
+ for (int i=0; i<strlen(line); i++)
+ if (line[i] == '\t') line[i] = ' ';
+
+ stream_write(outstream, line, strlen(line));
+ stream_putc(outstream, 0x00);
+ out_size += strlen(line) + 1;
+ free(line);
+ }
+
+ // end of pack
+ stream_fill(outstream, 0xff, 2);
+
+ // update the size in the head
+ stream_seek(outstream, rec_start + 2, SEEK_SET);
+ stream_putc(outstream, (out_size>>8) & 0xff);
+ stream_putc(outstream, out_size & 0xff);
+
+ return out_size + 4;
+}
+
+UINT16 get_odb(imgtool_stream *instream, imgtool_stream *outstream, UINT8 type, UINT8 file_id)
+{
+ UINT8 data, *buffer;
+ UINT16 out_size = 0;
+
+ if (file_id >= 0x90)
+ while (seek_next_record(instream, file_id))
+ {
+ stream_read(instream, &data, 1);
+ stream_seek(instream, 1, SEEK_CUR);
+ buffer = (UINT8*)malloc(data);
+ stream_read(instream, buffer, data);
+ stream_write(outstream, buffer, data);
+ stream_putc(outstream, '\r');
+ stream_putc(outstream, '\n');
+ free (buffer);
+ out_size += data;
+ }
+
+ return out_size;
+}
+
+UINT16 get_ob3(imgtool_stream *instream, imgtool_stream *outstream, UINT8 type, UINT8 file_id)
+{
+ UINT8 data, *buffer = NULL;
+ UINT16 size = 0;
+ static const char ob3_magic[3] = {'O', 'R', 'G'};
+
+ stream_read(instream, &data, 1);
+
+ if (data == 0x02)
+ {
+ stream_seek(instream, 1, SEEK_CUR);
+ size = get_long_rec_size(instream);
+ buffer = (UINT8*)malloc(size);
+ stream_read(instream, buffer, size);
+ }
+
+ stream_write(outstream, ob3_magic, 3);
+ stream_putc(outstream, (size>>8) & 0xff);
+ stream_putc(outstream, size & 0xff);
+ stream_putc(outstream, type | 0x80);
+
+ if (buffer)
+ {
+ stream_write(outstream, buffer, size);
+ free (buffer);
+ }
+
+ return size;
+}
+
+static imgtoolerr_t datapack_open( imgtool_image *image, imgtool_stream *stream)
+{
+ psion_pack *pack = (psion_pack*)imgtool_image_extra_bytes(image);
+ char opk_magic[4];
+
+ stream_read(stream, opk_magic, 4);
+
+ if(strcmp(opk_magic, "OPK\0"))
+ return IMGTOOLERR_UNEXPECTED;
+
+ pack->stream = stream;
+
+ if (update_pack_index(pack))
+ return IMGTOOLERR_SUCCESS;
+ else
+ return IMGTOOLERR_CORRUPTIMAGE;
+}
+
+static imgtoolerr_t datapack_create( imgtool_image *image, imgtool_stream *stream, option_resolution *opts)
+{
+ psion_pack *pack = (psion_pack*)imgtool_image_extra_bytes(image);
+ static const UINT8 opk_magic[4] = {'O', 'P', 'K', 0x00};
+ UINT8 pack_head[8] = {0x40, 0x00, 0x59, 0x01, 0x01, 0x01, 0x00, 0x00};
+ UINT16 checksum;
+
+ pack_head[0] |= (option_resolution_lookup_int(opts, 'R')) ? 0x00 : 0x02;
+ pack_head[0] |= (option_resolution_lookup_int(opts, 'P')) ? 0x04 : 0x00;
+ pack_head[0] |= (option_resolution_lookup_int(opts, 'W')) ? 0x00 : 0x08;
+ pack_head[0] |= (option_resolution_lookup_int(opts, 'B')) ? 0x00 : 0x10;
+ pack_head[0] |= (option_resolution_lookup_int(opts, 'C')) ? 0x20 : 0x00;
+ pack_head[1] = option_resolution_lookup_int(opts, 'S');
+
+ checksum = head_checksum(pack_head);
+
+ stream_write(stream, opk_magic, 4);
+ stream_fill(stream, 0x00, 2);
+ stream_write(stream, pack_head, 8);
+
+ stream_putc(stream, (checksum>>8) & 0xff);
+ stream_putc(stream, checksum & 0xff);
+
+ put_name_record(stream, "MAIN", 0x81, 0x90);
+
+ stream_fill(stream, 0xff, 2);
+
+ update_opk_head(stream);
+
+ pack->stream = stream;
+
+ if (update_pack_index(pack))
+ return IMGTOOLERR_SUCCESS;
+ else
+ return IMGTOOLERR_CORRUPTIMAGE;
+}
+
+static void datapack_close( imgtool_image *image)
+{
+ psion_pack *pack = (psion_pack*)imgtool_image_extra_bytes(image);
+
+ stream_close( pack->stream );
+}
+
+static imgtoolerr_t datapack_begin_enum(imgtool_directory *enumeration, const char *path)
+{
+ psion_iter *iter = (psion_iter*)imgtool_directory_extrabytes(enumeration);
+ iter->index = 0;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+static imgtoolerr_t datapack_next_enum(imgtool_directory *enumeration, imgtool_dirent *ent)
+{
+ imgtool_image *image = imgtool_directory_image(enumeration);
+ psion_pack *pack = (psion_pack*)imgtool_image_extra_bytes(image);
+ psion_iter *iter = (psion_iter*)imgtool_directory_extrabytes(enumeration);
+ UINT8 data = 0;
+
+ if (!pack->pack_index[iter->index].name_rec)
+ {
+ ent->eof = 1;
+ return IMGTOOLERR_SUCCESS;
+ }
+ memcpy(ent->filename, pack->pack_index[iter->index].filename, 8);
+ sprintf(ent->attr, "Type: %02x ID: %02x", pack->pack_index[iter->index].type, pack->pack_index[iter->index].id);
+
+ if (pack->pack_index[iter->index].data_rec)
+ {
+ stream_seek(pack->stream, pack->pack_index[iter->index].data_rec + 2, SEEK_SET);
+ ent->filesize = get_long_rec_size(pack->stream);
+ }
+
+ // seek all file's records
+ if (pack->pack_index[iter->index].id >= 0x90)
+ {
+ stream_seek(pack->stream, 0x10, SEEK_SET);
+ while (seek_next_record(pack->stream, pack->pack_index[iter->index].id))
+ {
+ stream_read(pack->stream, &data, 1);
+ stream_seek(pack->stream, data + 1, SEEK_CUR);
+ ent->filesize +=data;
+ }
+ }
+
+ iter->index++;
+ return IMGTOOLERR_SUCCESS;
+}
+
+static void datapack_close_enum( imgtool_directory *enumeration)
+{
+}
+
+static imgtoolerr_t datapack_free_space( imgtool_partition *partition, UINT64 *size)
+{
+ imgtool_image *image = imgtool_partition_image( partition);
+ psion_pack *pack = (psion_pack*)imgtool_image_extra_bytes(image);
+ UINT32 pack_size = 0;
+
+ stream_seek(pack->stream, 0x07, SEEK_SET);
+ stream_read(pack->stream, &pack_size, 1);
+
+ if (size)
+ *size = (pack_size * 0x2000) - pack->eop;
+
+ return IMGTOOLERR_SUCCESS;
+}
+
+static imgtoolerr_t datapack_read_file(imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *destf)
+{
+ imgtool_image *image = imgtool_partition_image(partition);
+ psion_pack *pack = (psion_pack*)imgtool_image_extra_bytes(image);
+ int index = seek_file_name(pack, filename);
+
+ if (index >= 0)
+ {
+ if ((pack->pack_index[index].type & 0x7f) == 0x01)
+ {
+ // ODB files
+ stream_seek(pack->stream, 0x10, SEEK_SET);
+ get_odb(pack->stream, destf, pack->pack_index[index].type, pack->pack_index[index].id);
+ }
+ else if ((pack->pack_index[index].type & 0x7f) == 0x03)
+ {
+ // OB3/OPL files
+ stream_seek(pack->stream, pack->pack_index[index].data_rec, SEEK_SET);
+ get_ob3(pack->stream, destf, pack->pack_index[index].type, pack->pack_index[index].id);
+ }
+ else
+ {
+ // Other files
+ return IMGTOOLERR_UNIMPLEMENTED;
+ }
+
+ return IMGTOOLERR_SUCCESS;
+ }
+ else
+ return IMGTOOLERR_FILENOTFOUND;
+}
+
+static imgtoolerr_t datapack_write_file( imgtool_partition *partition, const char *filename, const char *fork, imgtool_stream *sourcef, option_resolution *opts)
+{
+ imgtool_image *image = imgtool_partition_image(partition);
+ psion_pack *pack = (psion_pack*)imgtool_image_extra_bytes(image);
+ static const UINT8 data_head[4] = {0x02, 0x80, 0x00, 0x00};
+ UINT8 head[3];
+ UINT16 size = 0;
+ UINT8 type = option_resolution_lookup_int(opts, 'T');
+ UINT8 file_id = option_resolution_lookup_int(opts, 'I');
+
+ if (!pack->eop)
+ return IMGTOOLERR_CORRUPTIMAGE;
+
+ // if not file_id is specified get the first free (for ODB only)
+ if (file_id == 0 && type == 3)
+ {
+ file_id = get_free_file_id(pack);
+
+ if (file_id == 0xff)
+ return IMGTOOLERR_NOSPACE;
+ }
+
+ stream_read(sourcef, head, 3);
+ stream_seek(pack->stream, pack->eop, SEEK_SET);
+
+ if (type == 0)
+ type = (!strncmp((char*)head, "ORG", 3)) ? 1 : 2;
+
+ switch (type)
+ {
+ case 1: //OB3 file
+ put_name_record(pack->stream, filename, 0x83, file_id);
+ stream_write(pack->stream, data_head, 4);
+ size = put_ob3(sourcef, pack->stream);
+ break;
+ case 2: //OPL file
+ put_name_record(pack->stream, filename, 0x83, file_id);
+ stream_write(pack->stream, data_head, 4);
+ size = put_opl(sourcef, pack->stream);
+ break;
+ case 3: //ODB file
+ put_name_record(pack->stream, filename, 0x81, file_id);
+ size = put_odb(sourcef, pack->stream, file_id);
+ break;
+ }
+
+ if (type != 3)
+ {
+ // update the OB3/OPL long record size
+ stream_seek(pack->stream, pack->eop + 13, SEEK_SET);
+ stream_putc(pack->stream, (size>>8) & 0xff);
+ stream_putc(pack->stream, size & 0xff);
+ }
+
+ update_opk_head(pack->stream);
+
+ if (update_pack_index(pack))
+ return IMGTOOLERR_SUCCESS;
+ else
+ return IMGTOOLERR_CORRUPTIMAGE;
+}
+
+static imgtoolerr_t datapack_delete_file( imgtool_partition *partition, const char *filename)
+{
+ imgtool_image *image = imgtool_partition_image(partition);
+ psion_pack *pack = (psion_pack*)imgtool_image_extra_bytes(image);
+ int index = seek_file_name(pack, filename);
+
+ if (index >= 0)
+ {
+ // clear the bit 7 of the file type to mark the file as deleted
+ stream_seek(pack->stream, pack->pack_index[index].name_rec + 1, SEEK_SET);
+ stream_putc(pack->stream, pack->pack_index[index].type & 0x7f);
+
+ if (update_pack_index(pack))
+ return IMGTOOLERR_SUCCESS;
+ else
+ return IMGTOOLERR_CORRUPTIMAGE;
+ }
+ else
+ return IMGTOOLERR_FILENOTFOUND;
+}
+
+static OPTION_GUIDE_START( psion_create_optguide )
+ OPTION_ENUM_START( 'S', "size", "datapack size" )
+ OPTION_ENUM( 1, "8k", "8 kbyte" )
+ OPTION_ENUM( 2, "16k", "16 kbyts" )
+ OPTION_ENUM( 4, "32k", "32 kbyte" )
+ OPTION_ENUM( 8, "64k", "64 kbyte" )
+ OPTION_ENUM( 16, "128k", "128 kbyte" ) // only paged datapacks can have this size
+ OPTION_ENUM_END
+ OPTION_INT('R', "ram", "EPROM/RAM datapack" )
+ OPTION_INT('P', "paged", "linear/paged datapack" )
+ OPTION_INT('W', "protect", "write-protected datapack" )
+ OPTION_INT('B', "boot", "bootable datapack" )
+ OPTION_INT('C', "copy", "copyable datapack" )
+OPTION_GUIDE_END
+
+static OPTION_GUIDE_START( psion_write_optguide )
+ OPTION_ENUM_START( 'T', "type", "file type" )
+ OPTION_ENUM( 1, "OB3", "OB3 files" )
+ OPTION_ENUM( 2, "OPL", "OPL files" )
+ OPTION_ENUM( 3, "ODB", "ODB or text files" )
+ OPTION_ENUM_END
+ OPTION_INT( 'I', "id", "File ID" )
+OPTION_GUIDE_END
+
+void psion_get_info( const imgtool_class *imgclass, UINT32 state, union imgtoolinfo *info)
+{
+ switch (state)
+ {
+ // --- the following bits of info are returned as 64-bit signed integers ---
+ case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES : info->i = sizeof(psion_pack); break;
+ case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES : info->i = sizeof(psion_iter); break;
+
+ // --- the following bits of info are returned as pointers to data or functions ---
+ case IMGTOOLINFO_PTR_OPEN : info->open = datapack_open; break;
+ case IMGTOOLINFO_PTR_CREATE : info->create = datapack_create; break;
+ case IMGTOOLINFO_PTR_CLOSE : info->close = datapack_close; break;
+ case IMGTOOLINFO_PTR_BEGIN_ENUM : info->begin_enum = datapack_begin_enum; break;
+ case IMGTOOLINFO_PTR_NEXT_ENUM : info->next_enum = datapack_next_enum; break;
+ case IMGTOOLINFO_PTR_CLOSE_ENUM : info->close_enum = datapack_close_enum; break;
+ case IMGTOOLINFO_PTR_FREE_SPACE : info->free_space = datapack_free_space; break;
+ case IMGTOOLINFO_PTR_READ_FILE : info->read_file = datapack_read_file; break;
+ case IMGTOOLINFO_PTR_WRITE_FILE : info->write_file = datapack_write_file; break;
+ case IMGTOOLINFO_PTR_DELETE_FILE : info->delete_file = datapack_delete_file; break;
+ case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE : info->createimage_optguide = psion_create_optguide; break;
+ case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE : info->createimage_optguide = psion_write_optguide; break;
+
+ // --- the following bits of info are returned as NULL-terminated strings ---
+ case IMGTOOLINFO_STR_NAME : strcpy( info->s = imgtool_temp_str(), "psionpack"); break;
+ case IMGTOOLINFO_STR_DESCRIPTION : strcpy( info->s = imgtool_temp_str(), "Psion Organiser II Datapack"); break;
+ case IMGTOOLINFO_STR_FILE : strcpy( info->s = imgtool_temp_str(), __FILE__); break;
+ case IMGTOOLINFO_STR_FILE_EXTENSIONS : strcpy( info->s = imgtool_temp_str(), "opk"); break;
+ case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC : strcpy( info->s = imgtool_temp_str(), "S1/2/[4]/8/16;R[0]/1;P[0]/1;W0/[1];B[0]/1;C0/[1]"); break;
+ case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC : strcpy( info->s = imgtool_temp_str(), "T[1]/2/3;I[0]/145-255"); break;
+ }
+}