blob: 428882260c8f95b9462c6f1bd35807b0a900229d [file] [log] [blame]
/*
* xk5 :
* Extraction des fichiers à partir d'un fichier K7 (format MO)
*
* Copyright (c) 2011 Puls
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
typedef enum { FALSE, TRUE } bool_t;
enum {
E_CANT_READ_FILE = -100,
E_CANT_WRITE_FILE,
E_CHECKSUM,
E_END_OF_CASSETTE ,
E_READ_ERROR,
E_MEMORY
};
static const struct {
int err;
char str[33];
} elist [] = {
{ E_CANT_READ_FILE ,"Cannot read file" },
{ E_CANT_WRITE_FILE ,"Cannot write file" },
{ E_CHECKSUM ,"Checksum error" },
{ E_END_OF_CASSETTE ,"End of cassette reached" },
{ E_READ_ERROR ,"Read error" },
{ E_MEMORY ,"Memory error" },
{ 0 ,"Ok" }
};
static void
display_error (char *message, int err) {
int i = 0;
while ((err != elist[i].err) && (elist[i].err < 0))
i++;
(void)printf ("%s : %s\n", message, (char *)elist[i].str);
}
static void
clean_string (char *str) {
int i;
for (i=(int)strlen(str)-1; (i>=0)&&(str[i]<=' '); i--)
str[i] = '\0';
}
static int
k5_read_byte (FILE *file, uint8_t *byte)
/*@globals errno@*/ {
int err = 0;
*byte = (uint8_t)fgetc (file);
if ( feof (file) != 0) err = E_END_OF_CASSETTE;
if (ferror (file) != 0) err = E_READ_ERROR;
return err;
}
static int
k5_read_block (FILE *file, uint8_t *buf, uint8_t *size, uint8_t *type)
/*@globals errno@*/ {
int err = 0;
err = k5_read_byte (file, type);
if (err == 0) {
err = k5_read_byte (file, size);
*size -= 2;
if ((err == 0) && (*size != 0)) {
if (fread (buf, 1, *size, file) != *size) {
if ( feof (file) != 0) err = E_END_OF_CASSETTE;
if (ferror (file) != 0) err = E_READ_ERROR;
}
}
}
return err;
}
static int
k5_search_gap (FILE *file)
/*@globals errno@*/ {
int err = 0;
uint8_t byte = 0;
int count = 0;
while ((count < 5) && (err == 0)) {
err = k5_read_byte (file, &byte);
if (byte == 0x01) count++; else count = 0;
}
return err;
}
static uint8_t
k5_checksum (uint8_t *buf, uint8_t size){
uint8_t i;
uint8_t checksum ;
checksum = 0;
for (i = 0; i < size; i++)
checksum += buf[i];
return -checksum;
}
static int
k5_find_block (FILE *file, uint8_t *buf, uint8_t *size, uint8_t *type, uint8_t *checksum)
/*@globals errno@*/ {
int err = 0;
uint8_t byte = 0;
bool_t block_found = FALSE;
while ((err == 0) && (block_found == FALSE)) {
err = k5_search_gap (file);
byte = 0x01;
while ((err == 0) && (byte == 0x01))
err = k5_read_byte (file, &byte);
if ((err == 0) && (byte == 0x3c)) {
err = k5_read_byte (file, &byte);
if ((err == 0) && (byte == 0x5a)) {
err = k5_read_block (file, buf, size, type);
}
}
if (err == 0) {
*checksum = (uint8_t)fgetc (file);
if (ferror (file) != 0) err = E_READ_ERROR;
else block_found = TRUE;
}
}
return err;
}
static void
k5_pc_filename (uint8_t *buf, char *name) {
char pc_name[9] = "";
char pc_ext[4] = "";
strncat (pc_name, (char *)buf, 8);
strncat (pc_ext, (char *)buf+8, 3);
clean_string (pc_name);
clean_string (pc_ext);
*name = '\0';
(void)snprintf (name, 13, "%8s.%3s", pc_name, pc_ext);
}
static int
k5_searching (FILE *file, char *name)
/*@globals errno@*/ {
int err = 0;
uint8_t type = 0x01;
uint8_t size = 0;
uint8_t checksum = 0;
uint8_t buf[255];
memset (buf, 0x00, sizeof (&buf));
while ((type != 0x00) && (err == 0)) {
err = k5_find_block (file, buf, &size, &type, &checksum);
if (err == 0) {
if (checksum != k5_checksum (buf, size))
err = E_CHECKSUM;
k5_pc_filename (buf, name);
}
}
return err;
}
static int
k5_fload (FILE *file, uint8_t *buf, size_t *index, char *name)
/*@globals errno@*/ {
int err = 0;
uint8_t type = 0x01;
uint8_t size = 0;
uint8_t checksum = 0;
if ((file != NULL) && (buf != NULL)) {
err = k5_searching (file, name);
*index = 0;
while ((type != 0xff) && (err == 0)) {
err = k5_find_block (file, buf + *index, &size, &type, &checksum);
*index += (size_t)size;
}
}
return err;
}
static int
k5_extract (char *name)
/*@globals errno,stdout@*/ {
static FILE /*@dependent@*//*@null@*/*file = NULL;
static FILE /*@dependent@*//*@null@*/*fd_dest = NULL;
uint8_t *buf = NULL;
size_t size = 0;
int err = 0;
long int pos = 0;
char dstName[20] = "";
buf = (uint8_t*)calloc (1, 100000);
if (buf != NULL) {
file = fopen(name, "rb");
if (file != NULL) {
while (err == 0) {
err = k5_fload (file, buf, &size, dstName);
if (err == 0) {
pos = ftell (file);
(void)printf ("%07d '%s' (%d bytes)\n", (int)pos, dstName, (int)size);
fd_dest = fopen (dstName, "wb");
if (fd_dest != NULL) {
if (fwrite (buf, 1, size, fd_dest) != size)
err = E_READ_ERROR;
(void)fclose (fd_dest);
} else err = E_CANT_WRITE_FILE;
}
}
(void)fclose (file);
} else err = E_CANT_READ_FILE;
free (buf);
} else err = E_MEMORY;
return err;
}
int
main(int argc, char *argv[])
/*@globals errno,stdout@*/ {
char name[300+1] = "";
if (argc != 2) {
(void)printf (" xk5 version 0.1 (c) oct 2011 Puls\n");
(void)printf (" Usage : xk5 <k5file>\n");
} else {
(void)snprintf (name, 300, "%s", argv[1]);
clean_string (name);
display_error (name, k5_extract (name));
}
return EXIT_SUCCESS;
}