417 lines
11 KiB
C
417 lines
11 KiB
C
|
|
/*
|
|
* xDMS v1.3 - Portable DMS archive unpacker - Public Domain
|
|
* Written by Andre Rodrigues de la Rocha <adlroc@usa.net>
|
|
*
|
|
* Handles the processing of a single DMS archive
|
|
*
|
|
*/
|
|
|
|
|
|
#define HEADLEN 56
|
|
#define THLEN 20
|
|
#define TRACK_BUFFER_LEN 32000
|
|
#define TEMP_BUFFER_LEN 32000
|
|
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
#include "zfile.h"
|
|
|
|
#include "cdata.h"
|
|
#include "u_init.h"
|
|
#include "u_rle.h"
|
|
#include "u_quick.h"
|
|
#include "u_medium.h"
|
|
#include "u_deep.h"
|
|
#include "u_heavy.h"
|
|
#include "crc_csum.h"
|
|
#include "pfile.h"
|
|
|
|
|
|
|
|
static USHORT Process_Track(struct zfile *, struct zfile *, UCHAR *, UCHAR *, USHORT, USHORT, USHORT);
|
|
static USHORT Unpack_Track(UCHAR *, UCHAR *, USHORT, USHORT, UCHAR, UCHAR);
|
|
static void printbandiz(UCHAR *, USHORT);
|
|
static void dms_decrypt(UCHAR *, USHORT);
|
|
|
|
|
|
static char modes[7][7]={"NOCOMP","SIMPLE","QUICK ","MEDIUM","DEEP ","HEAVY1","HEAVY2"};
|
|
static USHORT PWDCRC;
|
|
|
|
UCHAR *text;
|
|
|
|
|
|
|
|
USHORT DMS_Process_File(struct zfile *fi, struct zfile *fo, USHORT cmd, USHORT opt, USHORT PCRC, USHORT pwd){
|
|
USHORT from, to, geninfo, c_version, cmode, hcrc, disktype, pv, ret;
|
|
ULONG pkfsize, unpkfsize;
|
|
UCHAR *b1, *b2;
|
|
time_t date;
|
|
|
|
|
|
b1 = (UCHAR *)calloc((size_t)TRACK_BUFFER_LEN,1);
|
|
if (!b1) return ERR_NOMEMORY;
|
|
b2 = (UCHAR *)calloc((size_t)TRACK_BUFFER_LEN,1);
|
|
if (!b2) {
|
|
free(b1);
|
|
return ERR_NOMEMORY;
|
|
}
|
|
text = (UCHAR *)calloc((size_t)TEMP_BUFFER_LEN,1);
|
|
if (!text) {
|
|
free(b1);
|
|
free(b2);
|
|
return ERR_NOMEMORY;
|
|
}
|
|
|
|
/* if iname is NULL, input is stdin; if oname is NULL, output is stdout */
|
|
|
|
if (zfile_fread(b1,1,HEADLEN,fi) != HEADLEN) {
|
|
free(b1);
|
|
free(b2);
|
|
free(text);
|
|
return ERR_SREAD;
|
|
}
|
|
|
|
if ( (b1[0] != 'D') || (b1[1] != 'M') || (b1[2] != 'S') || (b1[3] != '!') ) {
|
|
/* Check the first 4 bytes of file to see if it is "DMS!" */
|
|
free(b1);
|
|
free(b2);
|
|
free(text);
|
|
return ERR_NOTDMS;
|
|
}
|
|
|
|
hcrc = (USHORT)((b1[HEADLEN-2]<<8) | b1[HEADLEN-1]);
|
|
/* Header CRC */
|
|
|
|
if (hcrc != CreateCRC(b1+4,(ULONG)(HEADLEN-6))) {
|
|
free(b1);
|
|
free(b2);
|
|
free(text);
|
|
return ERR_HCRC;
|
|
}
|
|
|
|
geninfo = (USHORT) ((b1[10]<<8) | b1[11]); /* General info about archive */
|
|
date = (time_t) ((((ULONG)b1[12])<<24) | (((ULONG)b1[13])<<16) | (((ULONG)b1[14])<<8) | (ULONG)b1[15]); /* date in standard UNIX/ANSI format */
|
|
from = (USHORT) ((b1[16]<<8) | b1[17]); /* Lowest track in archive. May be incorrect if archive is "appended" */
|
|
to = (USHORT) ((b1[18]<<8) | b1[19]); /* Highest track in archive. May be incorrect if archive is "appended" */
|
|
|
|
pkfsize = (ULONG) ((((ULONG)b1[21])<<16) | (((ULONG)b1[22])<<8) | (ULONG)b1[23]); /* Length of total packed data as in archive */
|
|
unpkfsize = (ULONG) ((((ULONG)b1[25])<<16) | (((ULONG)b1[26])<<8) | (ULONG)b1[27]); /* Length of unpacked data. Usually 901120 bytes */
|
|
|
|
c_version = (USHORT) ((b1[46]<<8) | b1[47]); /* version of DMS used to generate it */
|
|
disktype = (USHORT) ((b1[50]<<8) | b1[51]); /* Type of compressed disk */
|
|
cmode = (USHORT) ((b1[52]<<8) | b1[53]); /* Compression mode mostly used in this archive */
|
|
|
|
PWDCRC = PCRC;
|
|
|
|
if ( (cmd == CMD_VIEW) || (cmd == CMD_VIEWFULL) ) {
|
|
|
|
pv = (USHORT)(c_version/100);
|
|
write_log(" Created with DMS version %d.%02d ",pv,c_version-pv*100);
|
|
if (geninfo & 0x80)
|
|
write_log("Registered\n");
|
|
else
|
|
write_log("Evaluation\n");
|
|
|
|
write_log(" Creation date : %s",ctime(&date));
|
|
write_log(" Lowest track in archive : %d\n",from);
|
|
write_log(" Highest track in archive : %d\n",to);
|
|
write_log(" Packed data size : %lu\n",pkfsize);
|
|
write_log(" Unpacked data size : %lu\n",unpkfsize);
|
|
write_log(" Disk type of archive : ");
|
|
|
|
/* The original DMS from SDS software (DMS up to 1.11) used other values */
|
|
/* in disk type to indicate formats as MS-DOS, AMax and Mac, but it was */
|
|
/* not suported for compression. It was for future expansion and was never */
|
|
/* used. The newer versions of DMS made by ParCon Software changed it to */
|
|
/* add support for new Amiga disk types. */
|
|
switch (disktype) {
|
|
case 0:
|
|
case 1:
|
|
/* Can also be a non-dos disk */
|
|
write_log("AmigaOS 1.0 OFS\n");
|
|
break;
|
|
case 2:
|
|
write_log("AmigaOS 2.0 FFS\n");
|
|
break;
|
|
case 3:
|
|
write_log("AmigaOS 3.0 OFS / International\n");
|
|
break;
|
|
case 4:
|
|
write_log("AmigaOS 3.0 FFS / International\n");
|
|
break;
|
|
case 5:
|
|
write_log("AmigaOS 3.0 OFS / Dir Cache\n");
|
|
break;
|
|
case 6:
|
|
write_log("AmigaOS 3.0 FFS / Dir Cache\n");
|
|
break;
|
|
case 7:
|
|
write_log("FMS Amiga System File\n");
|
|
break;
|
|
default:
|
|
write_log("Unknown\n");
|
|
}
|
|
|
|
write_log(" Compression mode used : ");
|
|
if (cmode>6)
|
|
write_log("Unknown !\n");
|
|
else
|
|
write_log("%s\n",modes[cmode]);
|
|
|
|
write_log(" General info : ");
|
|
if ((geninfo==0)||(geninfo==0x80)) write_log("None");
|
|
if (geninfo & 1) write_log("NoZero ");
|
|
if (geninfo & 2) write_log("Encrypted ");
|
|
if (geninfo & 4) write_log("Appends ");
|
|
if (geninfo & 8) write_log("Banner ");
|
|
if (geninfo & 16) write_log("HD ");
|
|
if (geninfo & 32) write_log("MS-DOS ");
|
|
if (geninfo & 64) write_log("DMS_DEV_Fixed ");
|
|
if (geninfo & 256) write_log("FILEID.DIZ");
|
|
write_log("\n");
|
|
|
|
write_log(" Info Header CRC : %04X\n\n",hcrc);
|
|
|
|
}
|
|
|
|
if (disktype == 7) {
|
|
/* It's not a DMS compressed disk image, but a FMS archive */
|
|
free(b1);
|
|
free(b2);
|
|
free(text);
|
|
return ERR_FMS;
|
|
}
|
|
|
|
|
|
if (cmd == CMD_VIEWFULL) {
|
|
write_log(" Track Plength Ulength Cmode USUM HCRC DCRC Cflag\n");
|
|
write_log(" ------ ------- ------- ------ ---- ---- ---- -----\n");
|
|
}
|
|
|
|
if (((cmd==CMD_UNPACK) || (cmd==CMD_SHOWBANNER)) && (geninfo & 2) && (!pwd))
|
|
return ERR_NOPASSWD;
|
|
|
|
ret=NO_PROBLEM;
|
|
|
|
Init_Decrunchers();
|
|
|
|
if (cmd != CMD_VIEW) {
|
|
if (cmd == CMD_SHOWBANNER) /* Banner is in the first track */
|
|
ret = Process_Track(fi,NULL,b1,b2,cmd,opt,(geninfo & 2)?pwd:0);
|
|
else {
|
|
while ( (ret=Process_Track(fi,fo,b1,b2,cmd,opt,(geninfo & 2)?pwd:0)) == NO_PROBLEM ) ;
|
|
}
|
|
}
|
|
|
|
if ((cmd == CMD_VIEWFULL) || (cmd == CMD_SHOWDIZ) || (cmd == CMD_SHOWBANNER)) write_log("\n");
|
|
|
|
if (ret == DMS_FILE_END) ret = NO_PROBLEM;
|
|
|
|
|
|
/* Used to give an error message, but I have seen some DMS */
|
|
/* files with texts or zeros at the end of the valid data */
|
|
/* So, when we find something that is not a track header, */
|
|
/* we suppose that the valid data is over. And say it's ok. */
|
|
if (ret == ERR_NOTTRACK) ret = NO_PROBLEM;
|
|
|
|
|
|
free(b1);
|
|
free(b2);
|
|
free(text);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
static USHORT Process_Track(struct zfile *fi, struct zfile *fo, UCHAR *b1, UCHAR *b2, USHORT cmd, USHORT opt, USHORT pwd){
|
|
USHORT hcrc, dcrc, usum, number, pklen1, pklen2, unpklen, l, r;
|
|
UCHAR cmode, flags;
|
|
|
|
|
|
l = (USHORT)zfile_fread(b1,1,THLEN,fi);
|
|
|
|
if (l != THLEN) {
|
|
if (l==0)
|
|
return DMS_FILE_END;
|
|
else
|
|
return ERR_SREAD;
|
|
}
|
|
|
|
/* "TR" identifies a Track Header */
|
|
if ((b1[0] != 'T')||(b1[1] != 'R')) return ERR_NOTTRACK;
|
|
|
|
/* Track Header CRC */
|
|
hcrc = (USHORT)((b1[THLEN-2] << 8) | b1[THLEN-1]);
|
|
|
|
if (CreateCRC(b1,(ULONG)(THLEN-2)) != hcrc) return ERR_THCRC;
|
|
|
|
number = (USHORT)((b1[2] << 8) | b1[3]); /* Number of track */
|
|
pklen1 = (USHORT)((b1[6] << 8) | b1[7]); /* Length of packed track data as in archive */
|
|
pklen2 = (USHORT)((b1[8] << 8) | b1[9]); /* Length of data after first unpacking */
|
|
unpklen = (USHORT)((b1[10] << 8) | b1[11]); /* Length of data after subsequent rle unpacking */
|
|
flags = b1[12]; /* control flags */
|
|
cmode = b1[13]; /* compression mode used */
|
|
usum = (USHORT)((b1[14] << 8) | b1[15]); /* Track Data CheckSum AFTER unpacking */
|
|
dcrc = (USHORT)((b1[16] << 8) | b1[17]); /* Track Data CRC BEFORE unpacking */
|
|
|
|
if (cmd == CMD_VIEWFULL) {
|
|
if (number==80)
|
|
write_log(" FileID ");
|
|
else if (number==0xffff)
|
|
write_log(" Banner ");
|
|
else if ((number==0) && (unpklen==1024))
|
|
write_log(" FakeBB ");
|
|
else
|
|
write_log(" %2d ",(short)number);
|
|
|
|
write_log("%5d %5d %s %04X %04X %04X %0d\n", pklen1, unpklen, modes[cmode], usum, hcrc, dcrc, flags);
|
|
}
|
|
|
|
if ((pklen1 > TRACK_BUFFER_LEN) || (pklen2 >TRACK_BUFFER_LEN) || (unpklen > TRACK_BUFFER_LEN)) return ERR_BIGTRACK;
|
|
|
|
if (zfile_fread(b1,1,(size_t)pklen1,fi) != pklen1) return ERR_SREAD;
|
|
|
|
if (CreateCRC(b1,(ULONG)pklen1) != dcrc) return ERR_TDCRC;
|
|
|
|
/* track 80 is FILEID.DIZ, track 0xffff (-1) is Banner */
|
|
/* and track 0 with 1024 bytes only is a fake boot block with more advertising */
|
|
/* FILE_ID.DIZ is never encrypted */
|
|
|
|
if (pwd && (number!=80)) dms_decrypt(b1,pklen1);
|
|
|
|
if ((cmd == CMD_UNPACK) && (number<80) && (unpklen>2048)) {
|
|
r = Unpack_Track(b1, b2, pklen2, unpklen, cmode, flags);
|
|
if (r != NO_PROBLEM)
|
|
if (pwd)
|
|
return ERR_BADPASSWD;
|
|
else
|
|
return r;
|
|
if (usum != Calc_CheckSum(b2,(ULONG)unpklen))
|
|
if (pwd)
|
|
return ERR_BADPASSWD;
|
|
else
|
|
return ERR_CSUM;
|
|
if (zfile_fwrite(b2,1,(size_t)unpklen,fo) != unpklen) return ERR_CANTWRITE;
|
|
}
|
|
|
|
if ((cmd == CMD_SHOWBANNER) && (number == 0xffff)){
|
|
r = Unpack_Track(b1, b2, pklen2, unpklen, cmode, flags);
|
|
if (r != NO_PROBLEM)
|
|
if (pwd)
|
|
return ERR_BADPASSWD;
|
|
else
|
|
return r;
|
|
if (usum != Calc_CheckSum(b2,(ULONG)unpklen))
|
|
if (pwd)
|
|
return ERR_BADPASSWD;
|
|
else
|
|
return ERR_CSUM;
|
|
printbandiz(b2,unpklen);
|
|
}
|
|
|
|
if ((cmd == CMD_SHOWDIZ) && (number == 80)) {
|
|
r = Unpack_Track(b1, b2, pklen2, unpklen, cmode, flags);
|
|
if (r != NO_PROBLEM) return r;
|
|
if (usum != Calc_CheckSum(b2,(ULONG)unpklen)) return ERR_CSUM;
|
|
printbandiz(b2,unpklen);
|
|
}
|
|
|
|
return NO_PROBLEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
static USHORT Unpack_Track(UCHAR *b1, UCHAR *b2, USHORT pklen2, USHORT unpklen, UCHAR cmode, UCHAR flags){
|
|
switch (cmode){
|
|
case 0:
|
|
/* No Compression */
|
|
memcpy(b2,b1,(size_t)unpklen);
|
|
break;
|
|
case 1:
|
|
/* Simple Compression */
|
|
if (Unpack_RLE(b1,b2,unpklen)) return ERR_BADDECR;
|
|
break;
|
|
case 2:
|
|
/* Quick Compression */
|
|
if (Unpack_QUICK(b1,b2,pklen2)) return ERR_BADDECR;
|
|
if (Unpack_RLE(b2,b1,unpklen)) return ERR_BADDECR;
|
|
memcpy(b2,b1,(size_t)unpklen);
|
|
break;
|
|
case 3:
|
|
/* Medium Compression */
|
|
if (Unpack_MEDIUM(b1,b2,pklen2)) return ERR_BADDECR;
|
|
if (Unpack_RLE(b2,b1,unpklen)) return ERR_BADDECR;
|
|
memcpy(b2,b1,(size_t)unpklen);
|
|
break;
|
|
case 4:
|
|
/* Deep Compression */
|
|
if (Unpack_DEEP(b1,b2,pklen2)) return ERR_BADDECR;
|
|
if (Unpack_RLE(b2,b1,unpklen)) return ERR_BADDECR;
|
|
memcpy(b2,b1,(size_t)unpklen);
|
|
break;
|
|
case 5:
|
|
case 6:
|
|
/* Heavy Compression */
|
|
if (cmode==5) {
|
|
/* Heavy 1 */
|
|
if (Unpack_HEAVY(b1,b2,flags & 7,pklen2)) return ERR_BADDECR;
|
|
} else {
|
|
/* Heavy 2 */
|
|
if (Unpack_HEAVY(b1,b2,flags | 8,pklen2)) return ERR_BADDECR;
|
|
}
|
|
if (flags & 4) {
|
|
/* Unpack with RLE only if this flag is set */
|
|
if (Unpack_RLE(b2,b1,unpklen)) return ERR_BADDECR;
|
|
memcpy(b2,b1,(size_t)unpklen);
|
|
}
|
|
break;
|
|
default:
|
|
return ERR_UNKNMODE;
|
|
}
|
|
|
|
if (!(flags & 1)) Init_Decrunchers();
|
|
|
|
return NO_PROBLEM;
|
|
|
|
}
|
|
|
|
|
|
/* DMS uses a lame encryption */
|
|
static void dms_decrypt(UCHAR *p, USHORT len){
|
|
USHORT t;
|
|
|
|
while (len--){
|
|
t = (USHORT) *p;
|
|
*p++ ^= (UCHAR)PWDCRC;
|
|
PWDCRC = (USHORT)((PWDCRC >> 1) + t);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void printbandiz(UCHAR *m, USHORT len){
|
|
UCHAR *i,*j;
|
|
|
|
i=j=m;
|
|
while (i<m+len) {
|
|
if (*i == 10) {
|
|
*i=0;
|
|
write_log("%s\n",j);
|
|
j=i+1;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
}
|
|
|
|
|