/* unmsa.c vi: set ts=8 sw=8: */
/* unmsa--Expands Magic Shadow Archiver .MSA into raw disk images
* Copyright (C) 2002--2004 Peter Backes
*
* 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 3 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, see .
*/
#include /* EXIT_FAILURE, EXIT_SUCCESS */
#include /* stderr, fprintf(), fread() */
#include /* memset() */
#include
#include
#define MSHSIZ 10
#define pop16b(s, d) (d = *s++ << 8, d |= *s++)
#define copy16b(s, d) (d = *s << 8 | s[1])
static char *file;
struct msainfo {
unsigned short ident, nsecpt, nside;
unsigned short strk, etrk;
};
static size_t freadz(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
size_t got = fread(ptr, size, nmemb, stream);
if (got == nmemb)
return got;
fprintf(stderr, "%s: fread failed, want=%u*%u, got=%u\n",
file, nmemb, size, got);
memset((unsigned char *)ptr + got * size, 0x00,
(nmemb - got) * size);
return got;
}
static int copy_rle(FILE *os, FILE *is, struct msainfo *header, int len)
{
unsigned char buffer[512], track[516];
int sec = 0, rle = 0, chr = '\0';
register unsigned char *top = track + sizeof track;
#ifdef EBUG
fprintf(stderr, "copy_rle\n");
#endif
while (sec++ < header->nsecpt) {
register unsigned char *tar = buffer;
#ifdef EBUG
fprintf(stderr, "sector: %d\n", sec);
#endif
while (tar < buffer + sizeof buffer) {
/* XXX doesn't check whether the input stream has
* enough data.
*/
if (rle != 0) {
*tar++ = chr;
rle--;
continue;
}
if (top >= track + sizeof track - 4) {
register int blksiz = len < 512 ? len : 512;
memcpy(track, track + sizeof track - 4, 4);
if (len < 0)
return len;
top -= 512;
len -= blksiz;
if (blksiz)
freadz(track + 4, blksiz, 1, is);
#ifdef EBUG
fprintf(stderr, "debug: reading %d bytes "
"rlap at %d.\n", blksiz, top - track);
#endif
}
if (*top == 0xE5) {
top++;
chr = *top++;
pop16b(top, rle);
continue;
}
*tar++ = *top++;
}
fwrite(buffer, sizeof buffer, 1, os);
}
#ifdef EBUG
fprintf(stderr, "len: %d\n", len);
#endif
if (len != 0)
return len;
return 0;
}
static void copy_verb(FILE *os, FILE *is, struct msainfo *header)
{
unsigned char track[512];
int sec = 0;
#ifdef EBUG
fprintf(stderr, "copy_verb\n");
#endif
while (sec++ < header->nsecpt) {
freadz(track, sizeof track, 1, is);
fwrite(track, sizeof track, 1, os);
}
}
int extrmsa(FILE *os, FILE *is)
{
struct msainfo header;
unsigned char buffer[MSHSIZ];
register unsigned char *top = buffer;
int trk, ntrks, result;
freadz(top, MSHSIZ, 1, is);
pop16b(top, header.ident);
pop16b(top, header.nsecpt);
pop16b(top, header.nside);
pop16b(top, header.strk);
pop16b(top, header.etrk);
if (header.ident != 0x0E0F) {
fprintf(stderr, "%s: bad format, skipping\n", file);
return EXIT_FAILURE;
}
#ifdef EBUG
fprintf(stderr, "nsecpt= %hu, nside= %hu, strk= %hu, etrk= %hu\n",
header.nsecpt, header.nside, header.strk, header.etrk);
#endif
header.nside++;
header.etrk++;
ntrks = header.etrk * header.nside;
for (trk = header.strk * header.nside; trk < ntrks; trk++) {
int len;
if (fread(buffer, 2, 1, is) != 1) {
fprintf(stderr, "%s: Couldn't read length "
"of track %d, side %d, skipping.\n",
file, trk / header.nside,
trk % header.nside);
return EXIT_FAILURE;
}
copy16b(buffer, len);
#ifdef EBUG
fprintf(stderr, "track %d, side %d, len= %d\n",
trk / header.nside, trk % header.nside, len);
#endif
if (len == header.nsecpt * 512)
copy_verb(os, is, &header);
else if ((result = copy_rle(os, is, &header, len))) {
fprintf(stderr, "%s: Couldn't expand track %d, "
"side %d, len %d: leftover %d, skipping.\n",
file, trk / header.nside,
trk % header.nside, len, result);
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS;
}
int main(int argc, char *argv[])
{
int optind;
for (optind = 1; optind < argc; optind++) {
int err;
char *ep = strrchr(argv[optind], '.');
FILE *is, *os;
file = argv[optind];
if (!ep || (strcmp(ep, ".msa") && strcmp(ep, ".MSA"))) {
fprintf(stderr, "Unidentifiable suffix on %s.\n",
argv[optind]);
return EXIT_FAILURE;
}
if (!(is = fopen(argv[optind], "rb"))) {
fprintf(stderr,
"Couldn't open %s for reading: %s.\n",
argv[optind], strerror(errno));
return EXIT_FAILURE;
}
strcpy(ep, ep[1] == 'm' ? ".img" : ".IMG");
if (!(os = fopen(argv[optind], "wb"))) {
fclose(is);
fprintf(stderr,
"Couldn't open %s for writing: %s.\n",
argv[optind], strerror(errno));
return EXIT_FAILURE;
}
err = extrmsa(os, is);
fclose(os);
fclose(is);
/*if (err)
return err;*/
}
return EXIT_SUCCESS;
}