aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ksiders/Makefile67
-rw-r--r--ksiders/README.txt77
-rw-r--r--ksiders/atdos.h137
-rw-r--r--ksiders/atr.c1422
-rw-r--r--ksiders/atr.h65
-rw-r--r--ksiders/atr.txt521
-rw-r--r--ksiders/atrdir.165
-rw-r--r--ksiders/atrdir.c89
-rw-r--r--ksiders/atrextr.164
-rw-r--r--ksiders/atrextr.c63
-rw-r--r--ksiders/atrsrc.zipbin0 -> 18785 bytes
-rw-r--r--ksiders/kboot.h61
-rw-r--r--ksiders/makeatr.192
-rw-r--r--ksiders/makeatr.c41
-rw-r--r--ksiders/sortatr.180
-rw-r--r--ksiders/sortatr.c39
-rw-r--r--ksiders/unmakatr.c43
17 files changed, 2926 insertions, 0 deletions
diff --git a/ksiders/Makefile b/ksiders/Makefile
new file mode 100644
index 0000000..674b3e6
--- /dev/null
+++ b/ksiders/Makefile
@@ -0,0 +1,67 @@
+
+# This Makefile has been tested with GNU make and /usr/ccs/bin/make
+# on a Solaris 2.6 (SunOS 5.6) system.
+
+CC=gcc
+COPT=-O2
+CFLAGS=-Wall $(COPT) -ansi -fpack-struct
+
+# -pedantic complains about some perfectly harmless signed/unsigned
+# char pointer stuff.
+
+# Note: -fpack-struct works on gcc's at least as old as 2.95.3
+# If you're not using gcc, find your compiler's equivalent option
+# (/Zp on Microsoft compilers).
+
+DESTDIR=
+PREFIX=/usr/local
+BINDIR=$(PREFIX)/bin
+MANDIR=$(PREFIX)/man
+MAN1DIR=$(MANDIR)/man1
+
+# some systems need this instead:
+#MANDIR=$(PREFIX)/share/man
+
+# Comment out next line if your system does not support gzip'ed man pages
+GZIP_MAN=y
+
+EXES=atrdir atrextr makeatr sortatr unmakatr
+MANS=atrdir atrextr makeatr sortatr
+
+all: $(EXES)
+
+clean:
+ rm -f $(EXES) atr.o tags *~ *.bak
+
+install: all
+ mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR) ;\
+ for exe in $(EXES) ; do \
+ strip $$exe ;\
+ cp $$exe $(DESTDIR)$(BINDIR)/$$exe ;\
+ done ;\
+ for man in $(MANS) ; do \
+ if [ "$(GZIP_MAN)" = "y" ]; then \
+ gzip -c < $$man.1 > $(DESTDIR)$(MAN1DIR)/$$man.1.gz ;\
+ else \
+ cp $$man.1 $(DESTDIR)$(MAN1DIR) ; \
+ fi ;\
+ done
+ if [ "$(GZIP_MAN)" = "y" ]; then \
+ cd $(DESTDIR)$(MAN1DIR) && rm -f unmakatr.1.gz && ln -s makeatr.1.gz unmakatr.1.gz ;\
+ else \
+ cd $(DESTDIR)$(MAN1DIR) && rm -f unmakatr.1 && ln -s makeatr.1 unmakatr.1 ;\
+ fi
+
+
+atr.o: atr.c atr.h atdos.h kboot.h
+
+atrdir: atrdir.c atr.o
+
+atrextr: atrextr.c atr.o
+
+makeatr: makeatr.c atr.o
+
+sortatr: sortatr.c atr.o
+
+unmakatr: unmakatr.c atr.o
+
diff --git a/ksiders/README.txt b/ksiders/README.txt
new file mode 100644
index 0000000..b6c5934
--- /dev/null
+++ b/ksiders/README.txt
@@ -0,0 +1,77 @@
+
+This is a quick & dirty Linux/UNIX port of Ken Siders' old Atr Utils
+package for MS-DOS.
+
+The original MS-DOS sources can be found in the file atrsrc.zip, or
+you can download them from Ken's page:
+ http://atari.ksiders.tzo.com/a8emulators.html
+
+Please do NOT contact Ken Siders about bugs or issues with these ported
+and modified versions of his utilities. They are provided as-is, by me,
+B. Watson (urchlay at urchlay dot com).
+
+The utilities are:
+
+atrdir - Get a directory of an ATR image
+atrextr - Extract files from an ATR image to the current directory
+makeatr - Make a bootable "K-file" disk from an Atari executable
+sortatr - Sort the directory of an ATR image in alphabetical order
+unmakatr - Reverse of makeatr: extracts the Atari exe from a K-file disk
+
+All utilities work only with AtariDOS 2.x or MyDOS compatible images.
+SpartaDOS, AtariDOS 3/4, DOS XE, and other non-MyDOS-compatible formats
+are not supported. Atari DOS 2.5 images are only partially supported.
+
+All utilities have been tested on Linux (with gcc-3.4.4) and Solaris 2.6
+(with gcc-2.95.3). They produce identical output on both platforms.
+However, Solaris 2.6 doesn't support gzipped man pages, so you have
+to install with "make install GZIP_MAN=n".
+
+I've written rudimentary man pages for the utilities. Any errors in the
+man pages are my own fault.
+
+I've made a few changes to the library (atr.c). Some are bugfixes, some
+are feature enhancements, and a few are just to make the utilities behave
+more like UNIX utilities are expected to behave.
+
+- AtariDirectory() (used by atrdir) now partly supports DOS 2.5 extended
+files (using sectors > 720). They are listed (with "<"), but their size
+is not (shows up as "??"). (The original DOS version was "blind" to
+files like these, like DOS 2.0S is).
+
+- AtariDirectory(): Also, MyDOS directories are now listed (with ":"),
+though their size is listed as "??".
+
+- AtariDirectory(): Stop listing files after the first unused entry,
+as Atari DOSes do. For normal disks, this really wasn't a problem, but
+e.g. the HomeSoft games disks store file data in the higher-numbered
+(unused) directory sectors, cause atrdir to print garbage to the terminal.
+
+- PatternMatch() (used by atrdir and atrextr) now always matches all
+files, if the wildcard pattern is "*" or "*.*". Also, wildcard patterns
+are now case-insensitive.
+
+- SortAtariDir() (used by sortatr) now correctly fills unused characters
+in the filename with spaces, as Atari DOSes do. Previously, they were
+filled with nulls (Atari heart character).
+
+- ExtractAtariFile() (used by atrextr) no longer includes a trailing
+dot in the extracted filename, if the file in the image has no 3-character
+extender.
+
+- ExtractAtariFile(): extract path now may be up to 4096 bytes, and
+UNIX-style / is used for delimiter instead of DOS \
+
+- atrextr: file argument is now optional (defaults to *.* if missing
+or blank).
+
+- atrextr: added optional dir argument. Files will be extracted to this
+directory (which will be created if necessary).
+
+- all utilities: exit status is now 0 for success, non-zero for any error.
+
+- atr.c (all utilities): Added support for detecting endianness and
+swapping bytes in start sector and sector count, if needed. Atari and
+PC are both little-endian platforms, so the DOS sources didn't need to
+support big-endian platforms. Tested on Solaris 2.6 on Sparc hardware
+(big-endian).
diff --git a/ksiders/atdos.h b/ksiders/atdos.h
new file mode 100644
index 0000000..34849b6
--- /dev/null
+++ b/ksiders/atdos.h
@@ -0,0 +1,137 @@
+/* Copyright 1997 Ken Siders */
+
+/* dos compatability types */
+#define DOS_ATARI 1
+#define DOS_MYDOS 2
+#define DOS_SPARTADOS 3
+#define DOS_LJK 4
+
+#define MAX_ATARI_FILES 64
+
+/* disk format types */
+#define FORMAT_SD 1
+#define FORMAT_ED 2
+#define FORMAT_DD 3
+#define FORMAT_SD_CUSTOM 4
+#define FORMAT_DD_CUSTOM 5
+#define FORMAT_DEFAULT 6
+
+/* open modes */
+#define ATARI_OPEN_READ 4
+#define ATARI_OPEN_WRITE 8
+#define ATARI_OPEN_DIR 6
+
+/* these will be in variables later */
+#define firstDirSector 361
+#define lastDirSector 368
+#define vtocSector 360
+#define vtocSector2 0
+#define dirEntrySize 16
+#define dirEntriesPerSector 8
+
+#define DELETED_FLAG 0x80
+#define INUSE_FLAG 0x40
+#define LOCKED_FLAG 0x20
+#define MYDOSDIR_FLAG 0x10
+#define OPENOUTPUT_FLAG 0x01
+#define DOS25EXT_FLAGS 0x03
+
+struct S_AtariDosDirEntry
+ {
+ char flag;
+ unsigned short sectorCount;
+ unsigned short startSector;
+ char fileName[8];
+ char extender[3];
+ };
+
+typedef struct S_AtariDosDirEntry AtariDosDirEntry;
+typedef AtariDosDirEntry *AtariDosDirEntryPtr;
+
+/* 20070518 bkw:
+ Nothing actually uses this struct, and it's got endianness
+ issues... */
+#if 0
+struct S_AtariDosLink
+ {
+ int fileNo:6;
+ int nextSector:10;
+ int shortSector:1;
+ int bytesInSector:7;
+
+ };
+
+typedef struct S_AtariDosLink AtariDosLink;
+typedef AtariDosLink *AtariDosLinkPtr;
+#endif
+
+struct S_AtariVtoc
+ {
+ byte reserved[10];
+ byte vtoc[118];
+ };
+
+
+
+
+struct S_AtariFile
+ {
+ AtrFilePtr atr;
+ unsigned short startSector;
+ unsigned short currentSector;
+ unsigned short sectorSize;
+ unsigned short numberOfSectors; /* no. of sectors in file */
+ unsigned long fileSize ; /* not used yet */
+ byte openFlag;
+ byte eofFlag;
+ short currentOffset;
+ short bytesData;
+ short sectorLinkOffset;
+ short fileNo;
+ unsigned char sectorBuffer[256];
+
+ };
+
+typedef struct S_AtariFile AtariFile;
+typedef AtariFile * AtariFilePtr;
+
+
+
+
+struct S_AtariFileInfo
+ {
+ unsigned long fileSize; /* not implemented */
+ unsigned short startSector;
+ unsigned short sectorCount;
+ char fileName[13];
+ int locked;
+ int attrib; /* internal */
+ int dirSector; /* internal */
+ int dirEntry; /* internal */
+ char *pattern; /* internal */
+ char *atrName; /* internal */
+ int dosType;
+ short fileNo;
+ int flag; /* from dos */
+ };
+
+typedef struct S_AtariFileInfo AtariFileInfo;
+typedef AtariFileInfo *AtariFileInfoPtr;
+
+
+
+/* function prototypes */
+
+void MakeFileName( char *result, char *fileName, char *extender );
+int PatternMatch( char *pattern, char *fileName, char *extender);
+int AtariFindFirst( char *atrName, unsigned attrib,
+ char *pattern, AtariFileInfoPtr fileInfo );
+int AtariFindNext( AtariFileInfoPtr fileInfo );
+AtariFilePtr OpenAtariFile( char *atrName, char *fileName, byte mode);
+int AtariDirectory( char *atrName, char *pattern);
+long ReadAtariFile( AtariFilePtr atFile, char *buffer, long bytes );
+int CloseAtariFile( AtariFilePtr atFile );
+int EofAtariFile( AtariFilePtr atFile );
+long AtariFileSize( char *atrFile, char *fileName );
+int FixAtariFileNo( char *atrName, char *fileName, int fileNo );
+
diff --git a/ksiders/atr.c b/ksiders/atr.c
new file mode 100644
index 0000000..2c3942f
--- /dev/null
+++ b/ksiders/atr.c
@@ -0,0 +1,1422 @@
+/*********************************************************************
+ ATR/XFD File handling library
+ (C) Copyright 1997 Ken Siders
+
+ This file can be used in any freeware or public domain software
+ as long as credit is given.
+
+
+History
+
+00.000 6/16/97 Initial version
+00.001 6/24/97 Fixed AtariFileSize, Added EofAtariFile and
+ ExtractAtariFile functions
+00.002 6/26/97 Added SortDirectory Function
+00.003 7/14/97 Fixed Double density to treat first 3 sectors as
+ Single Density.
+00.004 7/16/97 Added CreateBootAtr
+00.005 8/22/97 Added ExtractExeFromBootAtr
+00.006 9/04/97 Fix signature check
+
+*********************************************************************
+
+To do:
+
+1 Clean up warnings + make more portable
+2 Allow opening write-protected ATRs
+3 Allow more than one reference to an ATR file to be opened so more
+ than one atari file can be opened at once. (Keep a count)
+4 More specific error returns
+5 Implement XFD handling
+6 Create documentation
+7 Optimize if necessary
+8 Implement DCM images (maybe)
+
+
+*********************************************************************/
+
+#define wide 1
+
+
+/* compile with /Zp option */
+/* 20070518 bkw: or -fpack-struct on gcc */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "atr.h"
+#include "atdos.h"
+#include "kboot.h"
+
+AtrHeader hdr;
+
+int lastAtariError = 0;
+
+static unsigned char sectorBuffer[256];
+static int CompareName( const void *a, const void *b );
+static int verbose = 0;
+
+/* 20070518 bkw: min() isn't standard on Linux. If you're porting to
+ some other UNIX and get errors about min() already being defined,
+ just comment this out. */
+static int min(int a, int b) {
+ return ((a < b) ? a : b);
+}
+
+/* 20070518 bkw: As written, the DOS sources aren't portable to
+ big-endian architectures. To fix this, EndianFix() will swap
+ the high and low bytes of startSector and sectorCount in a
+ dir entry on a big-endian arch (and leave them alone on a little-
+ endian arch). Must be called just after reading, and just before
+ writing.
+
+ The Intel x86 arch and the Atari's 6502 both happen to be
+ little-endian, which is why Ken could ignore the endianness
+ issue in the original MS-DOS sources.
+ */
+#define LITTLE_END 0
+#define BIG_END 1
+
+static int InitEndian() {
+ /* code based on Wikipedia "Endianness" entry */
+ union {
+ short s;
+ char c[sizeof(short)];
+ } un;
+
+ un.s = 0x0102;
+
+ if(sizeof(short) == 2) {
+ if(un.c[0] == 1 && un.c[1] == 2)
+ return BIG_END;
+ else if(un.c[0] == 2 && un.c[1] == 1)
+ return LITTLE_END;
+ }
+
+ printf("WARNING: Can't determine endianness of this platform!\n");
+ printf("Assuming little-endian, hoping for the best. Expect trouble.\n");
+ return LITTLE_END;
+}
+
+static void EndianFix(AtariDosDirEntryPtr entry) {
+ static int endian = -1;
+ unsigned short tmp;
+
+ if(endian == -1)
+ endian = InitEndian();
+
+ /* don't do anything on little-endian platforms */
+ if(endian == LITTLE_END)
+ return;
+
+ tmp = entry->startSector;
+ entry->startSector = ((tmp & 0xff) << 8) | ((tmp & 0xff00) >> 8);
+
+ tmp = entry->sectorCount;
+ entry->sectorCount = ((tmp & 0xff) << 8) | ((tmp & 0xff00) >> 8);
+}
+
+/******************************************************************
+SetVerbose - Sets verbose flag, returns last value of flag
+******************************************************************/
+
+int SetVerbose( int verb )
+ {
+ int last;
+
+ last = verbose;
+ verbose = verb;
+ return(last);
+ }
+
+
+
+/******************************************************************
+ OpenAtr - opens ATR file specified for read/write (if writeable).
+ Returns Atr Pointer if successful, 0 if not
+******************************************************************/
+
+AtrFilePtr OpenAtr(char *file )
+ {
+ AtrFilePtr atr;
+ int bytes;
+ unsigned short signature;
+
+ atr = malloc(sizeof(AtrFile));
+
+ atr->dosType = DOS_ATARI; /* assume atari dos disk */
+
+
+ atr->atrIn = fopen(file, "rb+");
+ if ( !atr->atrIn )
+ {
+ free(atr);
+ return 0;
+ }
+ bytes = fread(&hdr, 1, 16, atr->atrIn);
+ if ( !bytes )
+ {
+ free(atr);
+ return 0;
+ }
+
+ signature = hdr.idLow | hdr.idHigh << 8;
+ atr->imageSize = 16L * (hdr.paraLow | hdr.paraHigh * 256L | hdr.paraHigher * 65536L);
+ atr->secSize = hdr.secSizeLow | hdr.secSizeHigh << 8;
+ atr->crc = hdr.crc1 | hdr.crc2 * 256L | hdr.crc3 * 65536L | hdr.crc4 *256L * 65536L ;
+ atr->sectorCount = atr->imageSize / atr->secSize;
+ atr->flags = hdr.flags;
+ atr->writeProtect = atr->flags&1;
+ atr->authenticated = (atr->flags >> 1) & 1;
+
+ if ( atr->sectorCount > 721 )
+ atr->dosType = DOS_MYDOS;
+
+ if ( signature == 0x296 )
+ return atr;
+ else
+ {
+ free(atr);
+ return 0;
+ }
+ return atr;
+ }
+
+
+/******************************************************************
+ CloseAtr - closes ATR file specified in an Atr Pointer from
+ and Atr Open. Returns 0 if successful
+******************************************************************/
+
+int CloseAtr( AtrFilePtr atr )
+ {
+ if ( atr )
+ return(fclose(atr->atrIn));
+ else
+ return 1;
+ }
+
+
+/******************************************************************
+ ReadSector - Reads specified sector from the ATR file specified
+ info buffer which must be big enough for the sector
+ size of teh file. Returns number of bytes read or
+ 0 if error.
+******************************************************************/
+
+int ReadSector(AtrFilePtr atr, unsigned short sector, char *buffer)
+ {
+ unsigned long pos;
+ size_t bytes;
+
+ if ( !atr )
+ {
+ lastAtariError = 12;
+ return 0;
+ }
+/* calculate offset into file */
+ if ( atr->secSize > 128 && sector > 3 )
+ pos = (unsigned long)(sector-4) * atr->secSize + 400L;
+ else
+ pos = (unsigned long)(sector-1) * 128L + 16;
+
+/* position file pointer at that offset */
+ if ( fseek(atr->atrIn, pos, SEEK_SET) )
+ {
+ lastAtariError = 13;
+ return 0;
+ }
+
+/* read the data */
+ bytes = fread(buffer, 1, atr->secSize, atr->atrIn);
+ if ( bytes & 127 )
+ {
+ lastAtariError = 14;
+ return 0;
+ }
+ return bytes;
+ }
+
+/******************************************************************
+ WriteSector - Writes specified sector from the ATR file specified
+ from buffer specified. Returns number of bytes
+ written or 0 if error. Image must be writeable.
+******************************************************************/
+
+int WriteSector(AtrFilePtr atr, unsigned short sector, char *buffer)
+ {
+ unsigned long pos;
+ size_t bytes;
+
+ if ( !atr )
+ {
+ lastAtariError = 12;
+ return 0;
+ }
+
+/* calculate offset into file */
+ if ( atr->secSize > 128 && sector > 3 )
+ pos = (unsigned long)(sector-4) * atr->secSize + 400L;
+ else
+ pos = (unsigned long)(sector-1) * 128L + 16;
+
+/* set file pointer to that position */
+ if ( fseek(atr->atrIn, pos, SEEK_SET) )
+ {
+ lastAtariError = 13;
+ return 0;
+ }
+
+/* sector # to high? */
+ if ( pos + atr->secSize > atr->imageSize )
+ {
+ lastAtariError = 15;
+ return 0;
+ }
+
+/* write the data */
+ bytes = fwrite(buffer, 1, atr->secSize, atr->atrIn);
+ if ( bytes & 127 )
+ {
+ lastAtariError = 14;
+ return 0;
+ }
+ return bytes;
+}
+
+/******************************************************************
+ CreateAtr - Creates an ATR file with parameters specified. Sector
+ size must be a multiple of 128 bytes. Return 0 for
+ success
+******************************************************************/
+
+int CreateAtr( char *file, unsigned short sectors,
+ unsigned short sectorSize )
+ {
+ FILE *fp;
+ AtrHeader hdr = {0};
+ unsigned long imageSize;
+ int bytes;
+
+/* sector size must be a multiple of 128 */
+ if ( sectorSize & 127 )
+ return 1;
+/* determine the file size for the image */
+
+ if ( sectorSize > 128 && sectors > 2)
+ imageSize = (unsigned long)(sectorSize-3) * sectors + 384;
+ else
+ imageSize = (unsigned long)sectorSize * sectors;
+
+/* create the file */
+ fp = fopen(file, "wb");
+ if ( !fp )
+ return 1;
+
+/* set up the ATR header */
+ hdr.idHigh = 0x02;
+ hdr.idLow = 0x96;
+ hdr.paraLow = (imageSize >> 4) & 255;
+ hdr.paraHigh = (imageSize >> 12) & 255;
+ hdr.paraHigher = imageSize >> 20;
+ hdr.secSizeLow = sectorSize & 255;
+ hdr.secSizeHigh = sectorSize >> 8;
+ bytes = fwrite(&hdr, 1, 16, fp);
+ if ( bytes != 16 )
+ return 1;
+
+/* seek to last position needed in file - 1 */
+ if ( fseek(fp,(unsigned long)sectors * sectorSize - 1 , SEEK_SET) )
+ return 1;
+/* write one null byte */
+ if ( fputc( 0, fp ) == EOF )
+ return 1;
+ if ( fclose(fp) )
+ return 1;
+ return 0;
+ }
+
+/******************************************************************
+ GetAtrInfo - returns info for an open ATR image via pointers.
+ non 0 returned is error.
+******************************************************************/
+
+int GetAtrInfo( AtrFilePtr atr, unsigned short *sectorSize,
+ unsigned short *sectorCount, byte *protected)
+ {
+ if ( !atr )
+ return 1;
+/* duh */
+ *sectorSize = atr->secSize;
+ *sectorCount = atr->sectorCount;
+ *protected = atr->writeProtect;
+ return 0;
+ }
+
+
+
+
+
+/*-----------------------------------------------------------------*/
+/* ATARI 8-bit File IO routines */
+/*-----------------------------------------------------------------*/
+
+/******************************************************************
+ MakeFileName - Creates a filename.ext string from a zero padded
+ raw fileName and extender. Result is stored in
+ string pointed to be result. There is no return
+ value.
+******************************************************************/
+
+void MakeFileName( char *result, char *fileName, char *extender )
+ {
+ int i;
+
+ for(i=0; i<8; i++)
+ {
+ if (fileName[i] == ' ' || !fileName[i] )
+ break;
+ *(result++) = fileName[i];
+ }
+ *(result++) = '.';
+ for(i=0; i<3; i++)
+ {
+ if (extender[i] == ' ' || !extender[i] )
+ break;
+ *(result++) = extender[i];
+ }
+ *(result++) = 0;
+ }
+
+
+/******************************************************************
+ PatternMatch - Returns 1 if fileName+extender matches pattern in
+ pattern. Wildcards are the standard '?' and '*'
+ as supported by all Atari Dos's. Returns 0 if
+ it does not match.
+******************************************************************/
+int PatternMatch( char *pattern, char *fileName, char *extender)
+ {
+ int i=0;
+ char file[13];
+
+ /* 20070518 bkw: Special case: "*" and "*.*" always match anything */
+ if(strcmp(pattern, "*.*") == 0 || strcmp(pattern, "*") == 0)
+ return 1;
+
+ MakeFileName(file, fileName, extender);
+
+ while (*pattern && file[i] )
+ {
+ if ( !file[i] && *pattern )
+ return 0;
+ if ( file[i] && !*pattern )
+ return 0;
+
+ if ( *pattern == '*')
+ {
+ while ( file[i] && file[i] != '.')
+ i++;
+ while ( *pattern && *pattern != '.')
+ pattern++;
+ if ( file[i] == '.' && *pattern == '.' )
+ {
+ pattern++;
+ i++;
+ continue;
+ }
+ continue;
+ }
+
+ if ( *pattern == '?' && file[i] != '.' )
+ {
+ i++;
+ pattern++;
+ continue;
+ }
+
+ if ( toupper(*pattern) != file[i] )
+ return 0;
+
+ i++;
+ pattern++;
+ }
+
+ if ( !*pattern && !file[i] )
+ return 1;
+ else
+ return 0;
+ }
+
+
+/******************************************************************
+ AtariFindFirst - Finds first match for pattern and sets struct
+ with file information. returns 0 for success,
+ -1 if not found, other for error. This is
+ similiar to _dosfindfist in the DOS world.
+******************************************************************/
+
+int AtariFindFirst( char *atrName, unsigned attrib,
+ char *pattern, AtariFileInfoPtr fileInfo )
+ {
+ char buffer[256];
+ AtrFilePtr atr;
+ int i,j;
+ AtariDosDirEntryPtr dirEntry;
+ unsigned short sectorSize, sectorCount;
+ byte protected;
+
+/* open the ATR image */
+ atr = OpenAtr(atrName);
+ if ( atr == NULL )
+ return 2;
+
+/* Get some info about the ATR image and save */
+ if ( GetAtrInfo( atr, &sectorSize, &sectorCount, &protected) )
+ {
+ free(atr);
+ return 3;
+ }
+
+/* look for the file in the directory, if found initilize the fileInfo
+ structure with data from the directory sector */
+
+ for( i = firstDirSector, fileInfo->fileNo = 0; i <= lastDirSector; i++ )
+ {
+ if (! ReadSector(atr, (unsigned short) i, buffer) )
+ return 4;
+ for( j=0; j< dirEntriesPerSector; j++, fileInfo->fileNo++ )
+ {
+ dirEntry = (AtariDosDirEntryPtr)(buffer + dirEntrySize * j );
+ EndianFix(dirEntry);
+ fileInfo->locked = (dirEntry->flag & LOCKED_FLAG) ? 1 : 0;
+ if (dirEntry->flag & DELETED_FLAG )
+ continue;
+ if ( (/* (dirEntry->flag == DOS25EXT_FLAGS) || */ (dirEntry->flag & INUSE_FLAG)) &&
+ PatternMatch(pattern,
+ dirEntry->fileName, dirEntry->extender) )
+ {
+ fileInfo->flag = dirEntry->flag;
+ fileInfo->startSector = dirEntry->startSector;
+ fileInfo->sectorCount = dirEntry->sectorCount;
+ fileInfo->dirSector = i;
+ fileInfo->dirEntry = j;
+ fileInfo->attrib = attrib;
+ fileInfo->pattern = pattern;
+ fileInfo->atrName = atrName;
+ MakeFileName( fileInfo->fileName, dirEntry->fileName,
+ dirEntry->extender);
+ if ( CloseAtr(atr) )
+ return 5;
+ return( 0 ); /* success */
+ }
+ }
+ }
+ if ( CloseAtr(atr) )
+ return 6;
+
+ return -1;
+ }
+
+/******************************************************************
+ AtariFindNext - Returns next matching file after previous
+ AtariFindFirst or AtariFindNext call. The fileinfo
+ structure passed should not be altered from the
+ previous call. Also the ATR file name and pattern
+ from the initial AtariFindFirst call must still be
+ in scope. Similiar to _dosfindnext in DOS world.
+******************************************************************/
+
+int AtariFindNext( AtariFileInfoPtr fileInfo )
+ {
+ char buffer[256];
+ AtrFilePtr atr;
+ int i,j;
+ AtariDosDirEntryPtr dirEntry;
+ unsigned short sectorSize, sectorCount;
+ byte protected;
+
+ atr = OpenAtr(fileInfo->atrName);
+ if ( atr == NULL )
+ return 1;
+
+ if ( GetAtrInfo( atr, &sectorSize, &sectorCount, &protected) )
+ {
+ free(atr);
+ return 2;
+ }
+ i = fileInfo->dirSector;
+ j = fileInfo->dirEntry;
+
+ j++;
+ if ( j >= dirEntriesPerSector )
+ {
+ j=0;
+ i++;
+ }
+
+ for( ; i <= lastDirSector; i++ , j = 0)
+ {
+ if (! ReadSector(atr, (unsigned short) i, buffer) )
+ return 3;
+ for( ; j< dirEntriesPerSector; j++, fileInfo->fileNo++ )
+ {
+ dirEntry = (AtariDosDirEntryPtr)(buffer + dirEntrySize * j );
+ EndianFix(dirEntry);
+ fileInfo->locked = (dirEntry->flag & LOCKED_FLAG) ? 1 : 0;
+ if (dirEntry->flag & DELETED_FLAG )
+ continue;
+ if ( (dirEntry->flag & INUSE_FLAG) && PatternMatch(fileInfo->pattern,
+ dirEntry->fileName, dirEntry->extender) )
+ {
+ fileInfo->flag = dirEntry->flag;
+ fileInfo->startSector = dirEntry->startSector;
+ fileInfo->sectorCount = dirEntry->sectorCount;
+ fileInfo->dirSector = i;
+ fileInfo->dirEntry = j;
+ MakeFileName( fileInfo->fileName, dirEntry->fileName,
+ dirEntry->extender);
+ if ( CloseAtr(atr) )
+ return 4;
+ return( 0 );
+ }
+ }
+ }
+ if ( CloseAtr(atr) )
+ return 5;
+
+ return -1;
+ }
+
+
+/******************************************************************
+ OpenAtariFile - Opens file in an ATR image in the mode specified
+ (ATARI_OPEN_READ, ATARI_OPEN_WRITE, or
+ ATARI_OPEN_DIR. Returns pointer to atari file
+ structure or 0 on error.
+******************************************************************/
+
+AtariFilePtr OpenAtariFile( char *atrName, char *fileName, byte mode)
+ {
+ AtariFilePtr atFile;
+ byte protected;
+ unsigned short sectorSize;
+ unsigned short sectorCount;
+ AtariFileInfo fileInfo;
+
+/* bad open mode? */
+ if ( mode != ATARI_OPEN_READ && mode != ATARI_OPEN_WRITE &&
+ mode != ATARI_OPEN_DIR )
+ {
+ lastAtariError = 2;
+ return NULL;
+ }
+
+ atFile = malloc(sizeof(AtariFile));
+/* open the atr image */
+ atFile->atr = OpenAtr(atrName);
+ if ( atFile->atr == NULL )
+ {
+ free(atFile);
+ lastAtariError = 1;
+ return NULL;
+ }
+/* get some info on the ATR file and store */
+ if ( GetAtrInfo( atFile->atr, &sectorSize, &sectorCount, &protected) )
+ {
+ CloseAtr(atFile->atr);
+ free(atFile);
+ lastAtariError = 3;
+ return NULL;
+ }
+
+/* set file parameters */
+ atFile->sectorSize = sectorSize;
+ atFile->openFlag = mode;
+ atFile->eofFlag = 0;
+
+/* is ATR write protected? (APE extension?) */
+ if ( protected && (mode & ATARI_OPEN_WRITE) )
+ {
+ CloseAtr(atFile->atr);
+ free(atFile);
+ lastAtariError = 4;
+ return NULL;
+ }
+
+/* read directory, find start sector and number of sectors and set
+ in atFile. Initialize current sector to start sector also */
+
+ if ( AtariFindFirst(atrName, 0, fileName, &fileInfo) )
+ {
+ lastAtariError = 5;
+ return NULL;
+ }
+
+/* is the file the ATR is lcoated in write protected? */
+ if ( fileInfo.locked && (mode & ATARI_OPEN_WRITE) )
+ {
+ lastAtariError = 6;
+ return NULL;
+ }
+
+/* set some file info data in the structure */
+ atFile->startSector = atFile->currentSector = fileInfo.startSector;
+ atFile->fileNo = fileInfo.fileNo;
+ atFile->numberOfSectors = fileInfo.sectorCount;
+ atFile->openFlag = mode;
+ atFile->currentOffset = 0;
+ if (sectorSize == 128)
+ atFile->sectorLinkOffset = 125;
+ else if (sectorSize == 256 )
+ atFile->sectorLinkOffset = 253;
+ else
+ {
+ lastAtariError = 7;
+ return NULL;
+ }
+ return atFile;
+ }
+
+
+/******************************************************************
+ ReadAtariFile - reads bytes bytes from the open atari file specified
+ in atFile and stores them in buffer. buffer must be big enough.
+ Returns bytes actually read.
+******************************************************************/
+
+long ReadAtariFile( AtariFilePtr atFile, char *buffer, long bytes )
+{
+ /* int lastSector = 0; */
+ long bytesRead = 0;
+
+ if ( !bytes || atFile->eofFlag)
+ return 0;
+ if ( !(atFile->openFlag & ATARI_OPEN_READ) )
+ return 0;
+ if ( !atFile->currentOffset )
+ {
+ /* read sector */
+ if (ReadSector(atFile->atr, atFile->currentSector, atFile->sectorBuffer) != atFile->sectorSize )
+ {
+ if ( !lastAtariError )
+ lastAtariError = 19;
+ return 0;
+ }
+ if ( atFile->sectorSize == 128 )
+ atFile->bytesData = atFile->sectorBuffer[atFile->sectorLinkOffset+2]
+ & 127;
+ else
+ atFile->bytesData = atFile->sectorBuffer[atFile->sectorLinkOffset+2];
+ }
+ while( bytes )
+ {
+ while ( atFile->currentOffset < atFile->bytesData && bytes)
+ {
+ *(buffer++) = atFile->sectorBuffer[atFile->currentOffset++];
+ bytes--;
+ bytesRead++;
+ }
+
+ if ( bytes )
+ {
+ /* read next sector */
+ atFile->currentOffset = 0;
+ if (atFile->atr->dosType == DOS_MYDOS)
+ atFile->currentSector =
+ (atFile->sectorBuffer[atFile->sectorLinkOffset] << 8) |
+ atFile->sectorBuffer[atFile->sectorLinkOffset+1];
+ else /* assume atari dos */
+ atFile->currentSector =
+ ((atFile->sectorBuffer[atFile->sectorLinkOffset] & 3) << 8) |
+ (atFile->sectorBuffer[atFile->sectorLinkOffset+1]);
+
+ if (!atFile->currentSector )
+ {
+ atFile->eofFlag = 1;
+ return bytesRead;
+ }
+
+ ReadSector(atFile->atr, atFile->currentSector, atFile->sectorBuffer);
+ if ( atFile->sectorSize == 128 )
+ atFile->bytesData = atFile->sectorBuffer[atFile->sectorLinkOffset+2] & 127;
+ else
+ atFile->bytesData = atFile->sectorBuffer[atFile->sectorLinkOffset+2];
+ }
+
+ }
+ return bytesRead;
+ }
+
+
+/******************************************************************
+ CloseAtariFile - Closes Atari File
+******************************************************************/
+
+int CloseAtariFile( AtariFilePtr atFile )
+ {
+ int stat;
+ /* simple enough */
+ stat = CloseAtr( atFile->atr );
+ free( atFile );
+ return stat;
+ }
+
+
+/******************************************************************
+ EofAtariFile - Returns 1 if at EOF of atari file, 0 if not
+******************************************************************/
+
+int EofAtariFile( AtariFilePtr atFile )
+ {
+ /* simple enough */
+ return atFile->eofFlag;
+ }
+
+/******************************************************************
+ AtariDirectory - Displays atari directory of disk image to screen
+ return 0 for success. atrName is the ATR file
+ name, pattern is mask to use. use "*.*" for all
+ files. Wide if non zero displays actual file
+ length instead of just a sector count.
+******************************************************************/
+
+int AtariDirectory( char *atrName, char *pattern)
+ {
+ char buffer[256];
+ char fileName[14];
+ byte protected;
+ unsigned short sectorSize;
+ unsigned short sectorCount;
+ AtrFilePtr atr;
+ int i,j,cnt = 0;
+ char locked;
+ long fileSize;
+ AtariDosDirEntryPtr dirEntry;
+
+ atr = OpenAtr(atrName);
+ if ( atr == NULL )
+ return 1;
+
+ if ( GetAtrInfo( atr, &sectorSize, &sectorCount, &protected) )
+ {
+ free(atr);
+ return 1;
+ }
+
+ printf("sector size = %hu sector count = %hu\n\n", sectorSize,
+ sectorCount);
+
+
+ printf("\nDirectory of '%s':\n\n", atrName);
+
+ if ( !wide )
+ {
+ printf("no f filename ext secs startSec\n");
+ printf("-- - -------- --- ---- --------\n");
+ }
+ else
+ {
+ printf("no f filename ext secs length startSec\n");
+ printf("-- - -------- --- ---- ------ --------\n");
+ }
+ for( i = firstDirSector; i <= lastDirSector; i++ )
+ {
+ char printbuf[101];
+ int done = 0;
+ if (! ReadSector(atr, (unsigned short) i, buffer) )
+ return 1;
+ for( j=0; j< dirEntriesPerSector; j++ )
+ {
+ dirEntry = (AtariDosDirEntryPtr)(buffer + dirEntrySize * j );
+ EndianFix(dirEntry);
+ locked = (dirEntry->flag & LOCKED_FLAG) ? '*' : ' ';
+ if (dirEntry->flag & DELETED_FLAG )
+ locked = 'D';
+ else if (dirEntry->flag & MYDOSDIR_FLAG ) {
+ locked = ':';
+ } else if(dirEntry->flag == DOS25EXT_FLAGS) {
+ locked = '<';
+ }
+ if ( (dirEntry->flag == DOS25EXT_FLAGS) ||
+ ((dirEntry->flag & (INUSE_FLAG | MYDOSDIR_FLAG)) &&
+ PatternMatch(pattern, dirEntry->fileName, dirEntry->extender)) )
+ {
+ if ( wide )
+ {
+ CloseAtr(atr);
+ MakeFileName(fileName, dirEntry->fileName, dirEntry->extender);
+ fileSize = AtariFileSize(atrName, fileName);
+ if(fileSize < 0)
+ sprintf(printbuf, "??");
+ else
+ sprintf(printbuf, "%-6ld", fileSize);
+
+ atr = OpenAtr(atrName);
+ if ( !atr )
+ return 1;
+ printf("%2u %c %-8.8s %-3.3s %4.3hu %-6s %hu\n", cnt, locked,
+ dirEntry->fileName, dirEntry->extender,
+ dirEntry->sectorCount, printbuf, dirEntry->startSector);
+ }
+ else
+ printf("%2u %c %-8.8s %-3.3s %4.3hu %hu\n", cnt, locked,
+ dirEntry->fileName, dirEntry->extender,
+ dirEntry->sectorCount, dirEntry->startSector);
+ } else {
+ /* 20070518 bkw: Atari DOS stops at the first "never used"
+ dirent, so do the same here. */
+ done = 1;
+ break;
+ }
+ cnt++;
+ }
+ if(done) break;
+ }
+
+ if ( CloseAtr(atr) )
+ return 1;
+
+ return 0;
+ }
+
+
+/******************************************************************
+ AtariFileSize - Returns size of atari file or -1 on error
+******************************************************************/
+
+long AtariFileSize( char *atrFile, char *fileName )
+ {
+ long count = 0;
+ long bytes;
+ static char buffer[16];
+ AtariFilePtr input;
+
+ /* open the atari file on the ATR image */
+ input = OpenAtariFile(atrFile, fileName, ATARI_OPEN_READ);
+ if ( input == NULL )
+ return -1;
+ /* count how many bytes we can actually read */
+ while( (bytes=ReadAtariFile(input, buffer, sizeof(buffer))) > 0 )
+ count+= bytes;
+ CloseAtariFile(input);
+ return(count);
+ }
+
+
+/******************************************************************
+ ExtractAtariFile - returns no. files extracted, -no for error.
+ file is stored with same name in dosPath
+ directory. (don't add the trailing '\').
+ Wildcards are allowed for atari file. Use NULL
+ for dosPath to extract to current directory
+******************************************************************/
+
+int ExtractAtariFile( char *atrFile, char *fileName, char *unixPath )
+{
+ int count = 0;
+ long bytes, bytesOut;
+ static char buffer[16];
+ AtariFilePtr input;
+ char outName[4096];
+ FILE *output;
+ AtariFileInfo info;
+
+
+ if ( !AtariFindFirst(atrFile, 0, fileName, &info) )
+ {
+ do {
+ if ( unixPath != NULL)
+ {
+ strcpy(outName, unixPath);
+ strcat(outName,"/");
+ }
+ else
+ outName[0] = 0;
+ strcat(outName, info.fileName);
+
+ /* 20070518 bkw: Get rid of trailing dots for files with no extension */
+ if(outName[strlen(outName)-1] == '.')
+ outName[strlen(outName)-1] = '\0';
+
+ output = fopen(outName, "wb");
+ if (output == NULL )
+ {
+ lastAtariError = 30;
+ return -count-1;
+ }
+ input = OpenAtariFile(atrFile, info.fileName, ATARI_OPEN_READ);
+ if ( input == NULL )
+ {
+ return -count-1;
+ }
+ if ( verbose )
+ printf("Extracting '%s'...", outName);
+ while( (bytes=ReadAtariFile(input, buffer, sizeof(buffer))) > 0 )
+ {
+ bytesOut = fwrite(buffer, 1, (int)bytes, output);
+ if ( bytes != bytesOut )
+ {
+ fclose( output );
+ CloseAtariFile(input);
+ lastAtariError = 31;
+ if (verbose )
+ printf("\n");
+ return -count-1;
+ }
+ }
+ fclose( output );
+ CloseAtariFile(input);
+ if ( verbose )
+ printf(" done\n");
+ count ++;
+ } while ( !AtariFindNext(&info) );
+ }
+ else
+ {
+ return 0;
+ }
+ return(count);
+}
+
+
+/******************************************************************
+ UpdateAtariFileNo - For atari dos, will fix the file no in each
+ sector within the file. For use after a
+ directory is sorted. returns 0 for success
+******************************************************************/
+
+int FixAtariFileNo( char *atrName, char *fileName, int fileNo )
+{
+ /* int cnt=0; */
+ AtariFilePtr atFile;
+
+ atFile = OpenAtariFile( atrName, fileName, ATARI_OPEN_READ);
+ if ( atFile == NULL )
+ return 1;
+
+ if ( atFile->atr->dosType != DOS_ATARI )
+ {
+ CloseAtariFile(atFile);
+ return 0;
+ }
+
+
+ while (atFile->currentSector)
+ {
+ if (ReadSector(atFile->atr, atFile->currentSector, sectorBuffer) != atFile->sectorSize )
+ {
+ if ( !lastAtariError )
+ lastAtariError = 19;
+ CloseAtariFile(atFile);
+ return 1;
+ }
+
+ /* set the file no in the file */
+ sectorBuffer[atFile->sectorLinkOffset] &= 3;
+ sectorBuffer[atFile->sectorLinkOffset] |= (atFile->fileNo<<2);
+
+ /* write the sector */
+ if (WriteSector(atFile->atr, atFile->currentSector, sectorBuffer) != atFile->sectorSize )
+ {
+ if ( !lastAtariError )
+ lastAtariError = 19;
+ CloseAtariFile(atFile);
+ return 1;
+ }
+
+ /* get next sector in link */
+ atFile->currentSector =
+ ((sectorBuffer[atFile->sectorLinkOffset] & 3) << 8) |
+ sectorBuffer[atFile->sectorLinkOffset+1];
+ }
+ CloseAtariFile(atFile);
+ return 0;
+ }
+
+
+
+
+
+
+
+static int ClearAtariDirectory( char *file );
+
+
+/******************************************************************
+ SortAtariDir -
+******************************************************************/
+
+int SortAtariDir( char *atrName )
+ {
+ AtrFilePtr atr;
+ char *pos;
+ AtariFileInfoPtr files[64];
+ AtariFileInfo info;
+ AtariDosDirEntryPtr entry;
+ int offset;
+ unsigned short sector;
+
+ int i, cnt = 0;
+
+ /* read file info for all files, allocate memory and store in array */
+
+ if ( !AtariFindFirst(atrName, 0, "*.*", &info) )
+ {
+ do {
+ files[cnt] = malloc( sizeof(info) );
+ memcpy( files[cnt], &info, sizeof(info) );
+ cnt ++;
+ } while ( !AtariFindNext(&info) );
+ }
+ else
+ {
+ return 1;
+ }
+
+ /* sort the files by name */
+
+ qsort( (void *)files, (size_t) cnt, (size_t) sizeof(AtariFileInfoPtr), CompareName );
+
+ /* clear out the directory */
+ if ( ClearAtariDirectory(atrName) )
+ return 1;
+
+ /* write the entries in sorted order to the directory sectors */
+
+ offset = 0;
+ sector = firstDirSector;
+
+ atr = OpenAtr( atrName );
+
+ for( i=0; i<cnt; i++ )
+ {
+ entry = (AtariDosDirEntryPtr) (sectorBuffer + offset );
+ entry->startSector = files[i]->startSector;
+ entry->sectorCount = files[i]->sectorCount;
+ entry->flag = files[i]->flag;
+ EndianFix(entry);
+ pos = strchr( files[i]->fileName, '.' );
+
+ /* 20070518 bkw: init to all spaces, else you get filenames
+ like "DOS\0\0\0\0\0SYS" (\0 shows up as Atari heart character) */
+ memset(entry->fileName, ' ', 8);
+ memset(entry->extender, ' ', 3);
+
+ if ( pos != NULL)
+ {
+
+ strncpy( entry->fileName, files[i]->fileName,
+ min(pos-files[i]->fileName,8) );
+ strncpy( entry->extender, pos+1, 3 );
+ }
+ else
+ {
+ strncpy( entry->fileName, files[i]->fileName, 8);
+ }
+ offset += 16;
+ if (offset >= 128)
+ {
+ if ( WriteSector( atr, sector, sectorBuffer) != atr->secSize )
+ {
+ CloseAtr( atr );
+ return 1;
+ }
+ sector ++;
+ offset = 0;
+ memset(sectorBuffer, 0, sizeof(sectorBuffer));
+ }
+ }
+ if ( offset > 0 )
+ if ( WriteSector( atr, sector, sectorBuffer) != atr->secSize )
+ {
+ CloseAtr( atr );
+ return 1;
+ }
+
+ CloseAtr( atr );
+
+
+ /* This should have no effect on Mydos extended format disks */
+ for( i=0; i<cnt; i++ )
+ {
+ if ( FixAtariFileNo( atrName, files[i]->fileName, i) )
+ return 1;
+ free( files[i] );
+ }
+ return 0;
+ }
+
+/* function for above used as arg to qsort */
+static int CompareName( const void *a, const void *b )
+ {
+ AtariFileInfoPtr aa = *(AtariFileInfoPtr *)a,
+ bb = *(AtariFileInfoPtr *)b;
+
+ return strcmp( aa->fileName, bb->fileName );
+ }
+
+/*******************************************************************
+ClearAtariDirectory - internal routine
+*******************************************************************/
+
+static int ClearAtariDirectory( char *file )
+ {
+ AtrFilePtr atr;
+ int i;
+
+ memset(sectorBuffer, 0, sizeof(sectorBuffer) );
+ atr = OpenAtr( file );
+
+ for( i = firstDirSector; i <= lastDirSector; i++ )
+ if ( WriteSector(atr, i, sectorBuffer) != atr->secSize )
+ return 1;
+ return 0;
+ CloseAtr(atr);
+ }
+
+
+/********************************************************************
+ CreateBootAtr - creates a minimally sized bootable ATR image from
+ an atari executable. The executable must not need
+ DOS to run.
+********************************************************************/
+
+int CreateBootAtr( char *atrName, char *fileName)
+ {
+ unsigned long fileSize;
+ unsigned long sectorCnt;
+ AtrHeader hdr;
+ unsigned long paras;
+ FILE * atrFile, *inFile;
+ size_t padding, bytes, bytes2;
+ struct stat fileInfo;
+ int status;
+ int first = 1;
+
+/* get file's size */
+
+ status = stat(fileName, &fileInfo);
+ if ( status )
+ return 11;
+ fileSize = (unsigned long) fileInfo.st_size;
+ if ( !fileSize )
+ return 12;
+
+/* determine number of sectors required */
+
+ sectorCnt = (unsigned short) ((fileSize + 127L) / 128L + 3L);
+ paras = sectorCnt * 16;
+
+/* create ATR header */
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.idLow = (byte) 0x96;
+ hdr.idHigh = (byte) 0x2;
+ hdr.paraLow = (byte) (paras & 0xFF);
+ hdr.paraHigh = (byte) ((paras >> 8) & 0xFF);
+ hdr.paraHigher = (byte) ((paras >> 16) & 0xFF);
+ hdr.secSizeLow = (byte) 128;
+
+/* open output file */
+ atrFile = fopen(atrName, "wb");
+ if ( atrFile == NULL )
+ return 1;
+
+/* Write the ATR Header */
+ bytes = fwrite(&hdr, 1, sizeof(hdr), atrFile);
+ if ( bytes != sizeof(hdr) )
+ {
+ fclose(atrFile);
+ return 2;
+ }
+
+/* plug the file size into the boot sectors at offset 9 (4 bytes)*/
+ bootData[9] = (byte)(fileSize & 255);
+ bootData[10] = (byte)((fileSize >> 8) & 255);
+ bootData[11] = (byte)((fileSize >> 16) & 255);
+ bootData[12] = 0;
+
+/* write the three boot sectors */
+ bytes = fwrite(bootData, 1, 384, atrFile);
+ if ( bytes != 384 )
+ {
+ fclose(atrFile);
+ return 6;
+ }
+
+/* open the input file and copy/append the file's data to output file */
+
+ inFile = fopen(fileName, "rb");
+ if ( inFile == NULL )
+ {
+ fclose(atrFile);
+ return 13;
+ }
+
+ bytes = 384;
+ while (bytes == 384)
+ {
+ bytes = fread(bootData, 1, 384, inFile);
+ if ( !bytes )
+ break;
+
+ /* 20070518 bkw: Make sure it really is an Atari bin load file */
+ if(first) {
+ if((bootData[0] != 0xff) && (bootData[1] != 0xff)) {
+ fclose(inFile);
+ fclose(atrFile);
+ return 20;
+ }
+
+ first = 0;
+ }
+
+ bytes2 = fwrite(bootData, 1, bytes, atrFile);
+ if ( bytes != bytes2 )
+ {
+ fclose(inFile);
+ fclose(atrFile);
+ return 19;
+ }
+ }
+ if ( !feof(inFile) )
+ {
+ fclose(inFile);
+ fclose(atrFile);
+ return 19;
+ }
+
+ fclose(inFile);
+
+
+/* pad to even sector size (data has no meaning) */
+ padding = (size_t) ((sectorCnt-3) * 128 - fileSize );
+ if ( padding )
+ {
+ bytes = fwrite(bootData, 1, padding, atrFile);
+ if ( bytes != padding )
+ {
+ fclose(atrFile);
+ return 7;
+ }
+ }
+
+/* close output */
+ fclose(atrFile);
+
+ return 0;
+ }
+
+/********************************************************************
+ExtractExeFromBootAtr - undoes a CreateBootAtr by extracting the
+ original executable
+returns 0 for error, or file length in bytes of file extracted
+!!This function needs to have code added to distinguish error types.
+********************************************************************/
+
+long ExtractExeFromBootAtr( char *atrName, char *fileName)
+ {
+ FILE *atrFile, *exeFile;
+ unsigned char *buffer;
+ AtrHeader hdr = {0};
+ /* long fileSize; */
+ size_t bytes,bytes2,readCnt;
+ unsigned long fSize, size;
+
+/* get some memory */
+ buffer = malloc(384);
+ if ( !buffer)
+ return 0;
+
+/* open the atr file */
+ atrFile = fopen(atrName, "rb");
+ if ( !atrFile )
+ {
+ free(buffer);
+ return 0;
+ }
+
+/* read Atr header */
+
+ if ( fread(&hdr, 1, 16, atrFile) != 16 )
+ {
+ free(buffer);
+ fclose(atrFile);
+ return 0;
+ }
+
+/* verify it is an ATR file */
+
+ if ( hdr.idHigh != 0x02 || hdr.idLow != 0x96 )
+ {
+ free(buffer);
+ fclose(atrFile);
+ return 0;
+ }
+
+/* read first 3 (boot) sectors */
+
+ if ( fread(buffer, 1, 384, atrFile) != 384 )
+ {
+ free(buffer);
+ fclose(atrFile);
+ return 0;
+ }
+
+/* set the file size in bootData from the file so we can compare*/
+ bootData[9] = buffer[9];
+ bootData[10] = buffer[10];
+ bootData[11] = buffer[11];
+ bootData[12] = buffer[12];
+
+/* check if ATR was created by MakeBootAtr */
+ if ( memcmp(buffer, bootData, 384) )
+ {
+ free(buffer);
+ fclose(atrFile);
+ return 0;
+ }
+/* Get size of file to extract */
+ fSize = size = ((unsigned long)buffer[9]|(((unsigned long)buffer[10])<<8)|(((unsigned long)buffer[11])<<16));
+
+/* Open output file */
+ exeFile = fopen(fileName, "wb");
+ if ( !exeFile )
+ {
+ fclose(atrFile);
+ free(buffer);
+ return 0;
+ }
+/* copy 'size' bytes from the Atr file to the exe file */
+ bytes = 384;
+ while (bytes == 384 && fSize)
+ {
+ readCnt = min(384, fSize);
+ bytes = fread(buffer, 1, readCnt, atrFile);
+ if ( !bytes )
+ break;
+ bytes2 = fwrite(buffer, 1, bytes, exeFile);
+ if ( bytes != readCnt )
+ {
+ fclose(exeFile);
+ fclose(atrFile);
+ free(buffer);
+ return 0;
+ }
+ fSize -= bytes;
+ }
+
+/* clean up and get out of here */
+ fclose(exeFile);
+ fclose(atrFile);
+ free(buffer);
+
+ return size;
+ }
+
+
diff --git a/ksiders/atr.h b/ksiders/atr.h
new file mode 100644
index 0000000..53ca6be
--- /dev/null
+++ b/ksiders/atr.h
@@ -0,0 +1,65 @@
+/* Copyright 1997 Ken Siders */
+
+#ifndef ATR_H_INCLUDED
+#define ATR_H_INCLUDED
+
+/* constants */
+#define ATR_HEADER_SIZE 16
+
+
+/* typedefs */
+typedef unsigned char byte;
+
+
+/* structure definitions */
+
+struct S_HDR
+{
+byte idLow, idHigh;
+byte paraLow, paraHigh;
+byte secSizeLow, secSizeHigh;
+byte paraHigher;
+byte crc1, crc2, crc3, crc4;
+byte unused1, unused2, unused3, unused4;
+byte flags;
+};
+
+typedef struct S_HDR AtrHeader;
+
+struct S_AtrFile {
+ FILE *atrIn;
+ unsigned long imageSize;
+ unsigned short secSize;
+ unsigned long crc;
+ unsigned long sectorCount;
+ byte flags;
+ byte writeProtect;
+ byte authenticated;
+ unsigned short currentSector;
+ unsigned char dosType;
+};
+
+typedef struct S_AtrFile AtrFile;
+typedef AtrFile *AtrFilePtr;
+
+
+/* function prototypes */
+AtrFilePtr OpenAtr(char *file );
+
+int CloseAtr( AtrFilePtr atr );
+int ReadSector(AtrFilePtr atr, unsigned short sector, char *buffer);
+int WriteSector(AtrFilePtr atr, unsigned short sector, char *buffer);
+int CreateAtr( char *file, unsigned short sectors,
+ unsigned short sectorSize );
+int GetAtrInfo( AtrFilePtr atr, unsigned short *sectorSize,
+ unsigned short *sectorCount, byte *protected);
+int CreateBootAtr( char *atrName, char *fileName);
+long ExtractExeFromBootAtr(char *, char *);
+
+int SortAtariDir( char *atrName );
+int SetVerbose( int verb );
+int ExtractAtariFile( char *atrFile, char *fileName, char *dosPath );
+
+#endif
+
+
diff --git a/ksiders/atr.txt b/ksiders/atr.txt
new file mode 100644
index 0000000..44c2b06
--- /dev/null
+++ b/ksiders/atr.txt
@@ -0,0 +1,521 @@
+All files Copyright 1997 Ken Siders
+
+Ken's ATR manipulation C library. This has only been used under Microsoft
+C/C++ version 8.0. It will probably work on 7.0 and possibly earlier
+versions. You are on your own for porting to other compilers.
+
+With Microsoft C, you must use the /Zp option to pack structure members
+on byte boundries. I have no idea how this translates to other compilers.
+
+
+To Do
+1 Clean up warnings + make more portable
+2 Allow opening write-protected ATRs
+3 Allow more than one reference to an ATR file to be opened so more
+ than one atari file can be opened at once.
+4 More specific error returns
+5 Implement XFD handling
+6 Create documentation
+7 Optimize if necessary
+8 Implement DCM images (maybe)
+
+----------------------------------------------------------------------------
+
+This source is a work in progress and in distributed for those wanting
+to write ATR related programs. I had planned to tidy them up a lot before
+releasing them, but have been sidetracked by other projects. Feel free to
+use them in your own freeware and public domain programs if credit is given.
+
+These have all been compiled with Microsoft C version 8.0. They should
+compile with 7.0 and possibly earlier versions. They may need
+some tweaking for use on other compilers. The /Zp option must be used
+on the Microsoft compiler to pack structures on byte boundries. I don't
+know how this is done on other compilers.
+
+Files:
+The file that does all of the work is ATR.C. The other programs do some
+basic processing and call fucntions in ATR.C to do the work.
+
+The other C files, all build the ATR utilities that are on my Atari
+web page. They, of course must be linked with the main ATR file.
+
+There are two header files: ATR.H and ATRDOS.H both of which will be
+required by most programs. ATR.H contains definitions for ATR image related
+functions. ATRDOS.H contains definitions for referencing Mydos and
+Atari Dos files located on ATR images. Hopefully the short descriptions
+in the source are enough to allow one to figure out what each function does.
+There is also a third header file with the Boot disk routine.
+
+
+Note: You cannot open write protected images yet and you can only have
+one ATR open at a time. If you Open an ATR and then call a function like
+ATRDirectory, an error will result. You must Close the ATR first, call
+the function and then reopen the ATR. This also means you can't have two
+files open in a single ATR at the same time. This will hopefully be
+rectified soon. Good Luck.
+
+
+
+mailto: ken_siders@compuserve.com
+http://ourworld.compuserve.com/homepages/ken_siders/atari.htm
+
+
+PS: I still am looking for info on other disk formats such as SpartaDOS.
+Also info on subdirectory format, how to distinquish Dos 2.5 enhanced
+density and MyDos enhanced density, etc.....
+
+-----------------------------------------------------------------------------
+
+***********************************************************************
+Structures
+***********************************************************************
+ATR File pointer structure definition. Most of this info comes from
+the ATR header:
+
+struct S_AtrFile {
+ FILE *atrIn; pointer to the file.
+ unsigned long imageSize; image size
+ unsigned short secSize; sector size: 128/256
+ unsigned long crc; crc - ATR extension (not used)
+ unsigned long sectorCount; number of sectors in image
+ byte flags; flags byte from ATR image.
+ byte writeProtect; image write protected?
+ byte authenticated; ATR extension (not used)
+ unsigned short currentSector; don't know what I have this here for.
+ unsigned char dosType; ataridos, mydos? see atr.h for constants
+};
+typedef struct S_AtrFile AtrFile;
+typedef AtrFile *AtrFilePtr;
+
+
+Atari File pointer structure definition. This is info required to read and
+write atari files in an ATR image as well as related functions.
+
+struct S_AtariFile
+ {
+ AtrFilePtr atr; atr file is in
+ unsigned short startSector; start sector of file
+ unsigned short currentSector; current sector pointer
+ unsigned short sectorSize; sector size: 128 or 256
+ unsigned short numberOfSectors; number of sectors in file
+ unsigned long fileSize ; size of file in bytes
+ byte openFlag; flag used in AtariOpenFile
+ byte eofFlag; set if at eof
+ short currentOffset; current offset into sector
+ short bytesData; bytes of data in current sector
+ short sectorLinkOffset; offset to link data. usually 125 or 253
+ short fileNo; file No for Atari Dos
+ unsigned char sectorBuffer[256]; buffer with current sector
+ };
+typedef struct S_AtariFile AtariFile;
+typedef AtariFile * AtariFilePtr;
+
+
+***********************************************************************
+Image Functions - These functions are used for low level access to
+ ATR disk images.
+***********************************************************************
+
+OpenAtr AtrFilePtr OpenAtr(char *file )
+
+ Description:
+ Opens an ATR image so sectors can be read or written or information
+ on the image can be obtained.
+
+ Parameters:
+ file - the filename of the ATR. Use the full drive and pathname if
+ the file is not in the current directory. You must add the
+ .ATR extension.
+
+ Returns:
+ An ATR file pointer that is used with other functions to access the
+ image's data.
+
+
+CloseAtr int CloseAtr( AtrFilePtr atr )
+
+ Description:
+ Closes an ATR image opened with OpenATR.
+
+ Parameters:
+ atr - the ATR file pointer that was returned by the OpenAtr function.
+
+ Returns:
+ 0 for success, 1 if an error occured.
+
+
+ReadSector int ReadSector(AtrFilePtr atr, unsigned short sector,
+ char *buffer)
+
+ Description:
+ Reads specified sector from the ATR file specified into buffer which
+ must be big enough for the sector size of the ATR image file (128 or
+ 256 bytes).
+
+ Parameters:
+ atr - Atr file pointer returned from an OpenAtr function call.
+ sector - Sector number to read.
+ buffer - A pointer that points to a buffer large enough to hold a
+ sector of data. Usually you want this to be 256 bytes.
+
+ Returns:
+ Number of bytes read or 0 if error.
+
+
+WriteSector int WriteSector(AtrFilePtr atr, unsigned short sector,
+ char *buffer)
+
+ Description:
+ Writes specified sector from the ATR file specified from buffer
+ specified.
+
+ Parameters:
+ atr - Atr file pointer returned from an OpenAtr function call.
+ sector - Sector number to write.
+ buffer - A pointer that points to a buffer containing the sector
+ data to write. Usually 128 or 256 bytes.
+
+ Returns:
+ Number of bytes written or 0 if error.
+
+
+CreateAtr int CreateAtr( char *file, unsigned short sectors,
+ unsigned short sectorSize )
+
+ Description:
+ Creates an new ATR file with parameters specified.
+
+ Parameters:
+ file - Name of the ATR file to create. If file exists it will
+ be overwritten.
+ sectors - The number of sectors in the image. Common values are
+ 720 (single/double density) or 1040 (1050 double) but
+ smaller an huge images may also be created.
+ sectorSize - This should be 128 (single/1050 double density) or
+ 256 (double density).
+
+ Returns:
+ 0 for success, 1 on failure.
+
+
+GetAtrInfo int GetAtrInfo( AtrFilePtr atr, unsigned short *sectorSize,
+ unsigned short *sectorCount, byte *protected)
+
+ Descriptions:
+ Returns the sector size, and sector count for an image. It also
+ returns info if the disk is write protected or not. (Write protect
+ status is not fully implemented yet.)
+
+ Parameters:
+ atr - the ATR file pointer that was returned by the OpenAtr function.
+ sectorSize - A pointer to a variable in which to return the sector
+ size.
+ sectorCount - A pointer to a variable in which to return the sector
+ count.
+ protected - A pointer to a variable in which to return the write
+ protect status. 1 = protected, 0 = not. If the image
+ write protected or if the APE extension bit for write
+ protect is set, this will be 1. Not implemented yet.
+
+ Returns: 0 for success, 1 if error.
+
+
+AtariFindFirst int AtariFindFirst( char *atrName, unsigned attrib,
+ char *pattern,
+ AtariFileInfoPtr fileInfo )
+ Description:
+ Finds first match for pattern and sets struct with file information.
+ For those using Microsoft C compilers, this is similiar to
+ _dosfindfirst.
+
+ Parameters:
+ atrName - Name of ATR image in which to look for the file.
+ attrib - Not used yet. Use 0.
+ pattern - The atari file to look for. * and ? are accepted as normal
+ atari wildcards. Use only and 8.3 file name, no drive or
+ directory specifications.
+ fileInfo - A pointer to your file info structure in which to return
+ the information. This structure will also be use by
+ the AtariFindNext function.
+
+ Returns:
+ 0 if a match was found.
+ -1 if a match was not found.
+ a positive number if an error occurred.
+
+
+AtariFindNext int AtariFindFirst( AtariFileInfoPtr fileInfo )
+
+ Description:
+ Searches for the next match after a previous AtariFindFirst or
+ AtariFindNext. Sets struct with file information. For those using
+ Microsoft C compilers, this is similiar to _dosfindnext. The
+ fileInfo structure should not be modified prior to calling this
+ function.
+
+ Parameters:
+ fileInfo - A pointer to your file info structure in which to return
+ the information. This structure should still have
+ unmodified info from the last AtariFindFirst or
+ AtariFindNext function call.
+
+ Returns:
+ 0 if a match was found.
+ -1 if a match was not found.
+ a positive number if an error occurred.
+
+CreateBootAtr - int CreateBootAtr( char *atrName, char *fileName)
+
+ Description:
+ Will create a minimally sized bootable ATR image from an Atari
+ Executable. The ATR file will be just long enough to hold
+ three boot sectors and the file's data. The executable must
+ consist of only one file and not need DOS for any reason. Note:
+ Not all Atari computer and peripheral emulators may support
+ non-standard sized images. When booting from the image, the
+ screen will turn red if an error occurs.
+
+ Parameters:
+ atrName - The ATR file name.
+ fileName - The name of the MSDOS file to convert.
+
+ Returns:
+ 0 for success.
+
+
+ExtractExeFromBootAtr - long ExtractExeFromBootAtr( char *atrName,
+ char *fileName)
+
+ Description:
+ Undoes a CreateBootAtr by extracting the original executable from
+ the ATR image.
+
+ Parameters:
+ atrName - ATR file name of a disk created with CreateBootAtr.
+ fileName - The name of the MSDOS file to write the output to.
+
+ Returns:
+ Returns 0 for error, or file length in bytes of file extracted.
+ An error will result if the file was not created with CreateBootAtr.
+
+
+***********************************************************************
+Dos Functions - These functions are used to reference atari files
+ stored on ATR disk images.
+***********************************************************************
+
+OpenAtariFile - AtariFilePtr OpenAtariFile( char *atrName, char *fileName,
+ byte mode)
+
+ Description:
+ Opens file in an ATR image in the mode specified: ATARI_OPEN_READ,
+ ATARI_OPEN_WRITE, or ATARI_OPEN_DIR. ATARI_OPEN_DIR is not supported
+ yet.
+
+ Parameters:
+ atrName - Name of ATR image in which to look for the file.
+ fileName - Filename of the atari file in the ATR image to open.
+ mode - ATARI_OPEN_READ to open the file for read, ATARI_OPEN_WRITE
+ to open it for write. "Or" the two together for both.
+ ATARI_OPEN_DIR is not implemented yet.
+
+ Returns:
+ An Atari file pointer that can be used in functions to read or
+ write from the file. NULL is returned on error.
+
+
+ReadAtariFile - long ReadAtariFile( AtariFilePtr atFile, char *buffer,
+ long bytes )
+
+ Description:
+ Reads bytes bytes from an open atari file (opened with OpenAtariFile).
+
+ Parameters:
+ atFile - An Atari file pointer that was set from an OpenAtariFile function
+ call
+ buffer - A pointer to a buffer to read the bytes into. Must be big
+ enough to hold the number of bytes requested.
+ bytes - Number of bytes to read.
+
+ Returns:
+ Number of bytes actually read. May be less than bytes if EOF was
+ reached. You can use EofAtariFile to see if the EOF was reached
+ or if an error occurred.
+
+
+CloseAtariFile - int CloseAtariFile( AtariFilePtr atFile )
+
+ Description:
+ Closes an atari file opened with OpenAtariFile.
+
+ Parameters:
+ atFile - An atari file pointer used in an OpenAtariFile function
+ call.
+
+ Returns:
+ 0 if successful.
+
+
+EofAtariuFile - int EofAtariFile( AtariFilePtr atFile )
+
+ Description:
+ Determines if pointer is at the end of an atari file.
+
+ Parameters:
+ atFile - An atari file pointer returned from an OpenAtariFile
+ function call.
+
+ Returns:
+ Returns 1 if at EOF of atari file, 0 if not
+
+
+AtariDirectory - int AtariDirectory( char *atrName, char *pattern)
+
+ Description:
+ Displays atari directory of disk image to screen. The disk image
+ must be Atari Dos, MyDos, or compatable.
+
+ Parameters:
+ atrName - The ATR file name.
+ pattern - filename mask to use. use "*.*" for all files.
+
+ Returns:
+ 0 for no errors.
+
+
+AtariFileSize long AtariFileSize( char *atrFile, char *fileName )
+
+ Description:
+ Get the actual length in bytes of an atari file in an ATR image.
+
+ Parameters:
+ atrName - The ATR file name.
+ fileName - The atari filename in 8.3 to look for. No wildcards.
+
+ Returns:
+ Length of file in bytes or -1 for error.
+
+
+ExtractAtariFile - int ExtractAtariFile( char *atrFile, char *fileName,
+ char *dosPath )
+
+ Description:
+ Extracts an Atari file from an ATR image. Atari Dos and MyDos
+ formats are supported for the image.
+
+ Parameters:
+ atrName - The ATR file name.
+ fileName - Atari filename in 8.3 format. Wildcards * and ? are
+ allowed. If verbose is on, files will be displayed
+ as they are extracted.
+ dosPath - Directory to store the file in. File is stored with same
+ name in dosPath directory. (don't add the trailing '\').
+ Use NULL for dosPath to extract to current directory.
+
+ Returns:
+ number of file extracted. If the result is negative, an error
+ occured but the Absolute value of that number of files were
+ extracted successfully before the error occurred.
+
+
+FixAtariFileNo - int FixAtariFileNo( char *atrName, char *fileName,
+ int fileNo )
+
+ Description - For atari dos, will fix the file no in each sector within
+ the file. Used internally after a directory is sorted but
+ their may be other uses. For larger MyDos disks the
+ function has no effect. I currently don't know how to
+ distinquish DOS 2.5 1050 double density disks and Mydos
+ disks so this may not work on Dos 2.5 1050 double density
+ disks.
+
+ Parameters:
+ atrName - The ATR file name.
+ fileName - Atari filename in 8.3 format to fix. No wildcard's
+ allowed.
+ fileNo - File number to give the file. This could probably be
+ made to be automatic.
+
+ Returns:
+ 0 for success.
+
+
+SortAtariDir - int SortAtariDir( char *atrName )
+
+ Description:
+ Sorts the files in an ATR image. BUT: 1. May not work on Dos 2.5
+ 1050 double density disks. 2. Doesn't allow DOS.SYS and DUP.SYS,
+ or other files to be specified not to be sorted.
+
+ Parameters:
+ atrName - The ATR file name.
+
+ Returns:
+ 0 for success.
+
+
+
+***********************************************************************
+Other Functions - Other functions.
+***********************************************************************
+
+
+SetVerbose int SetVerbose( int verb )
+
+ Description:
+ Used to set verbose flag to 1 (on) or 0 (off). Currently the only
+ function that uses this is ExtractAtariFile.
+
+ Parameters:
+ verb - 1 for verbose, 0 for not verbose
+
+ Returns:
+ Previous state of verbose flag.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ksiders/atrdir.1 b/ksiders/atrdir.1
new file mode 100644
index 0000000..6e2d0bf
--- /dev/null
+++ b/ksiders/atrdir.1
@@ -0,0 +1,65 @@
+.TH ATRDIR "1" "April 2007" "atrdir" "Ken Siders' Atari 8-bit Tools"
+.SH NAME
+\fBatrdir\fR \- print the directory of an ATR disk image
+.SH SYNOPSIS
+\fBatrdir\fR \fIimagefile.atr\fR [\fIwildcard\fR]
+.SH DESCRIPTION
+\fBatrdir\fR prints the directory of an Atari DOS 2.x or MyDOS compatible
+ATR disk image. Sample output:
+.PP
+.nf
+---------------------------------------------------------------------
+Directory of dos_20s.atr:*.*:
+sector size = 128 sector count = 720
+
+
+Directory of 'dos_20s.atr':
+
+no f filename ext secs length startSec
+-- - -------- --- ---- ------ --------
+ 0 * DOS SYS 039 4875 4
+ 1 * DUP SYS 042 5126 43
+ 2 AUTORUN SYS 001 88 85
+---------------------------------------------------------------------
+.fi
+.PP
+The \fIf\fR column contains an asterisk (\fI*\fR) for a locked file,
+a \fID\fR for a deleted file, \fI:\fR for a MyDOS subdirectory,
+\fI<\fR for a DOS 2.5 file that uses sectors above 720,
+or nothing for any other file.
+.PP
+\fIwildcard\fR is an optional glob pattern, such as \fI*.sys\fR. If not
+given, the default is \fI*.*\fR. \fIwildcard\fR is case insensitive;
+\fI*.txt\fR is equivalent to \fI*.TXT\fR. Most shells will require the argument
+to be quoted, to avoid the shell's normal wildcard expansion. The
+globbing semantics are similar to those of Atari DOS or POSIX sh, but
+are not identical to either.
+.PP
+\fBatrdir\fR does not take any other options.
+.SH NOTES
+.PP
+DOS 2.5 disk images are not fully supported. Specifically, \fBatrdir\fR
+is unable to determine the size of files that use sectors above 720 (those
+that are listed with \fI<\fR). Instead, the size will be reported as \fB??\fR.
+.PP
+MyDOS large disk images (aka hard disk images) are supported.
+.PP
+XFD format images are not supported.
+.PP
+The current version of \fBatrdir\fR cannot determine the size of a MyDOS
+directory. Instead, the size is reported as \fB??\fR.
+.PP
+\fBatrdir\fR cannot print the contents of MyDOS subdirectories.
+.PP
+Exit status is zero for success, non-zero for failure.
+.SH AUTHOR
+Ken Siders <\fBatari8bit@columbus.rr.com\fR>
+.PP
+Man page by B. Watson <\fBurchlay@urchlay.com\fR>
+.SH SEE ALSO
+\&\fIatrextr\fR\|(1), \&\fImakeatr\fR\|(1), \&\fIsortatr\fR\|(1),
+\&\fIunmakatr\fR\|(1).
+.PP
+Ken Siders Atari 8\-bit page:
+.br
+http://atari.ksiders.tzo.com/a8emulators.html
diff --git a/ksiders/atrdir.c b/ksiders/atrdir.c
new file mode 100644
index 0000000..62920c0
--- /dev/null
+++ b/ksiders/atrdir.c
@@ -0,0 +1,89 @@
+/* Copyright 1997 Ken Siders */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "atr.h"
+#include "atdos.h"
+
+void help(void)
+ {
+ printf("usage: atrdir atrname.atr [filespec]\n");
+ printf("\n");
+ printf("\n\n");
+ }
+
+
+int main( int argc, char **argv)
+ {
+ AtrFilePtr atr;
+ int minCnt;
+ char option;
+ static char mask[80];
+
+
+ printf("AtrDir Version 0.9u (c)1997 Ken Siders\n");
+ printf("Ported and modified by B. Watson, 2007\n");
+ printf("This program may be freely distributed\n\n");
+
+ if (argc < 2)
+ {
+ help();
+ exit(2);
+ }
+
+ if (argv[1][0] == '-')
+ {
+ option = argv[1][1];
+ minCnt = 3;
+ }
+ else
+ {
+ option = 0;
+ minCnt = 2;
+ }
+
+ if ( argc != minCnt && argc != minCnt + 1)
+ {
+ help();
+ exit(4);
+ }
+
+ if (option && option != 'W' && option != 'w' )
+ {
+ help();
+ exit(5);
+ }
+
+ atr = OpenAtr(argv[minCnt-1]);
+ if (atr == 0)
+ {
+ printf("Error reading ATR file: %s\n", argv[1]);
+ exit(2);
+ }
+ else
+ {
+ CloseAtr(atr);
+ }
+
+ if (argc == minCnt)
+ strcpy(mask, "*.*");
+ else
+ strcpy(mask, argv[minCnt]);
+ printf("Directory of %s:%s:\n", argv[minCnt-1], mask);
+ AtariDirectory( argv[minCnt-1], mask);
+
+
+
+
+
+
+
+ /* printf("\n%d file(s)\n", count); */
+
+ exit(0);
+ }
+
+
+
+
diff --git a/ksiders/atrextr.1 b/ksiders/atrextr.1
new file mode 100644
index 0000000..9912f76
--- /dev/null
+++ b/ksiders/atrextr.1
@@ -0,0 +1,64 @@
+.TH ATREXTR "1" "April 2007" "atrextr" "Ken Siders' Atari 8-bit Tools"
+.SH NAME
+\fBatrextr\fR \- extract files from an ATR image
+
+.SH SYNOPSIS
+\fBatrextr\fR \fIimagefile.atr\fR [\fIfile\fR] [\fIdir\fR]
+
+.SH DESCRIPTION
+\fBatrextr\fR extracts one or more files from an Atari DOS 2.x or MyDOS
+compatible ATR disk image. Extracted files are written to the current
+directory.
+.PP
+\fIfile\fR is an optional filename or wildcard pattern,
+such as \fIautorun.sys\fR or \fI*.sys\fR. If not
+given, the default is \fI*.*\fR. \fIfile\fR is case insensitive:
+\fIreadme.txt\fR is equivalent to \fIREADME.TXT\fR, and
+\fI*.txt\fR is equivalent to \fI*.TXT\fR. If a wildcard is used,
+most shells will require the argument
+to be quoted, to avoid the shell's normal wildcard expansion. The
+wildcard semantics are similar to those of Atari DOS or POSIX sh, but
+are not identical to either.
+.PP
+\fIdir\fR is the directory to extract files to. If omitted, the default
+is the current directory. If \fIdir\fR is to be given, \fIfile\fR must
+also be given (although it may be specified as \fI""\fR, equivalent
+to \fI"*.*"\fR).
+.PP
+\fBatrextr\fR does not take any other options.
+.SH NOTES
+DOS 2.5 disk images are not fully supported. Specifically, \fBatrextr\fR
+is unable to extract files that use sectors above 720 (those
+that are listed with \fI<\fR, in \fBatrdir\fR or Atari DOS 2.5). These
+files will be silently skipped, exactly as Atari DOS 2.0S does when reading
+a 2.5 enhanced density disk.
+.PP
+MyDOS subdirectories are not supported. Only files in the root directory
+of a disk image can be extracted.
+.PP
+MyDOS large disk images (aka hard disk images) are supported, though
+without subdirectory support, \fBatrextr\fR can't extract most of the
+files in a typical hard disk image.
+.PP
+XFD format images are not supported. Neither are SpartaDOS, Atari DOS 3/4,
+DOS XE, or other non-standard DOS formats.
+.PP
+There should be options to translate Atari EOL characters to and from
+UNIX newlines, and to convert the extracted filenames to lowercase.
+.PP
+Exit status is zero if all files were extracted successfully, or
+non-zero if any errors occurred. In case of a non-zero return,
+some files may still have been extracted (though some or all of them
+may be truncated or corrupt).
+.SH AUTHOR
+Ken Siders <\fBatari8bit@columbus.rr.com\fR>
+.PP
+Man page by B. Watson <\fBurchlay@urchlay.com\fR>
+
+.SH SEE ALSO
+\&\fIatrdir\fR\|(1), \&\fImakeatr\fR\|(1), \&\fIsortatr\fR\|(1),
+\&\fIunmakatr\fR\|(1).
+.PP
+Ken Siders Atari 8-bit page:
+.br
+http://atari.ksiders.tzo.com/a8emulators.html
diff --git a/ksiders/atrextr.c b/ksiders/atrextr.c
new file mode 100644
index 0000000..2aeb898
--- /dev/null
+++ b/ksiders/atrextr.c
@@ -0,0 +1,63 @@
+/* Copyright 1997 Ken Siders */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "atr.h"
+#include "atdos.h"
+
+int main( int argc, char **argv) {
+ AtrFilePtr atr;
+ int stat = 0, ret = 0;
+ char *file = "*.*";
+ char *path = NULL;
+
+ printf("AtrExtr Version 0.9u (c)1997 Ken Siders\n");
+ printf("Ported and modified by B. Watson, 2007\n");
+ printf("This program may be freely distributed\n\n");
+
+ if(argc < 2 || argc > 4) {
+ printf("usage: atrextr atrname.atr [file] [dir]\n"
+ "Wildcards may be used for file (default: *.*)\n"
+ "dir will be created, if it does not exist\n");
+ exit(-1);
+ }
+
+ atr = OpenAtr(argv[1]);
+ if(atr == 0) {
+ printf("Error reading ATR file: %s\n", argv[1]);
+ CloseAtr(atr);
+ exit(2);
+ }
+
+ CloseAtr(atr);
+
+ if(argc >= 3 && argv[2][0])
+ file = argv[2];
+
+ if(argc == 4) {
+ path = argv[3];
+ mkdir(path, 0777); /* ignore errors (ExtractAtariFile() will fail instead) */
+ }
+
+ while ( !stat ) {
+ SetVerbose(1);
+ stat = ExtractAtariFile( argv[1], file, path );
+ SetVerbose(0);
+ }
+
+ if( stat < 0 ) {
+ printf("\nError encountered when extracting file(s)\n");
+ printf("some files may have been extracted incorrectly\n");
+ stat = -stat;
+ ret = 1;
+ }
+
+ printf("\n%d file(s) extracted\n", stat);
+
+ exit(ret);
+}
+
+
diff --git a/ksiders/atrsrc.zip b/ksiders/atrsrc.zip
new file mode 100644
index 0000000..7a70fb5
--- /dev/null
+++ b/ksiders/atrsrc.zip
Binary files differ
diff --git a/ksiders/kboot.h b/ksiders/kboot.h
new file mode 100644
index 0000000..02576f5
--- /dev/null
+++ b/ksiders/kboot.h
@@ -0,0 +1,61 @@
+/* Copyright 1997 Ken Siders */
+
+/* This is the code used in the first three (boot) sectors when
+ creating an ATR from an Atari executable
+
+History:
+ Ver 0.01 07/17/97 - Initial version
+*/
+
+
+unsigned char bootData[384] = {
+ 0x00, 0x03, 0x00, 0x07, 0x14, 0x07, 0x4C, 0x14,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x46, 0x8D,
+ 0xC6, 0x02, 0xD0, 0xFE, 0xA0, 0x00, 0xA9, 0x6B,
+ 0x91, 0x58, 0x20, 0xD9, 0x07, 0xB0, 0xEE, 0x20,
+ 0xC4, 0x07, 0xAD, 0x7A, 0x08, 0x0D, 0x76, 0x08,
+ 0xD0, 0xE3, 0xA5, 0x80, 0x8D, 0xE0, 0x02, 0xA5,
+ 0x81, 0x8D, 0xE1, 0x02, 0xA9, 0x00, 0x8D, 0xE2,
+ 0x02, 0x8D, 0xE3, 0x02, 0x20, 0xEB, 0x07, 0xB0,
+ 0xCC, 0xA0, 0x00, 0x91, 0x80, 0xA5, 0x80, 0xC5,
+ 0x82, 0xD0, 0x06, 0xA5, 0x81, 0xC5, 0x83, 0xF0,
+ 0x08, 0xE6, 0x80, 0xD0, 0x02, 0xE6, 0x81, 0xD0,
+ 0xE3, 0xAD, 0x76, 0x08, 0xD0, 0xAF, 0xAD, 0xE2,
+ 0x02, 0x8D, 0x70, 0x07, 0x0D, 0xE3, 0x02, 0xF0,
+ 0x0E, 0xAD, 0xE3, 0x02, 0x8D, 0x71, 0x07, 0x20,
+ 0xFF, 0xFF, 0xAD, 0x7A, 0x08, 0xD0, 0x13, 0xA9,
+ 0x00, 0x8D, 0xE2, 0x02, 0x8D, 0xE3, 0x02, 0x20,
+ 0xAE, 0x07, 0xAD, 0x7A, 0x08, 0xD0, 0x03, 0x4C,
+ 0x3C, 0x07, 0xA9, 0x00, 0x85, 0x80, 0x85, 0x81,
+ 0x85, 0x82, 0x85, 0x83, 0xAD, 0xE0, 0x02, 0x85,
+ 0x0A, 0x85, 0x0C, 0xAD, 0xE1, 0x02, 0x85, 0x0B,
+ 0x85, 0x0D, 0xA9, 0x01, 0x85, 0x09, 0xA9, 0x00,
+ 0x8D, 0x44, 0x02, 0x6C, 0xE0, 0x02, 0x20, 0xEB,
+ 0x07, 0x85, 0x80, 0x20, 0xEB, 0x07, 0x85, 0x81,
+ 0xA5, 0x80, 0xC9, 0xFF, 0xD0, 0x10, 0xA5, 0x81,
+ 0xC9, 0xFF, 0xD0, 0x0A, 0x20, 0xEB, 0x07, 0x85,
+ 0x80, 0x20, 0xEB, 0x07, 0x85, 0x81, 0x20, 0xEB,
+ 0x07, 0x85, 0x82, 0x20, 0xEB, 0x07, 0x85, 0x83,
+ 0x60, 0x20, 0xEB, 0x07, 0xC9, 0xFF, 0xD0, 0x09,
+ 0x20, 0xEB, 0x07, 0xC9, 0xFF, 0xD0, 0x02, 0x18,
+ 0x60, 0x38, 0x60, 0xAD, 0x09, 0x07, 0x0D, 0x0A,
+ 0x07, 0x0D, 0x0B, 0x07, 0xF0, 0x79, 0xAC, 0x79,
+ 0x08, 0x10, 0x50, 0xEE, 0x77, 0x08, 0xD0, 0x03,
+ 0xEE, 0x78, 0x08, 0xA9, 0x31, 0x8D, 0x00, 0x03,
+ 0xA9, 0x01, 0x8D, 0x01, 0x03, 0xA9, 0x52, 0x8D,
+ 0x02, 0x03, 0xA9, 0x40, 0x8D, 0x03, 0x03, 0xA9,
+ 0x80, 0x8D, 0x04, 0x03, 0xA9, 0x08, 0x8D, 0x05,
+ 0x03, 0xA9, 0x1F, 0x8D, 0x06, 0x03, 0xA9, 0x80,
+ 0x8D, 0x08, 0x03, 0xA9, 0x00, 0x8D, 0x09, 0x03,
+ 0xAD, 0x77, 0x08, 0x8D, 0x0A, 0x03, 0xAD, 0x78,
+ 0x08, 0x8D, 0x0B, 0x03, 0x20, 0x59, 0xE4, 0xAD,
+ 0x03, 0x03, 0xC9, 0x02, 0xB0, 0x22, 0xA0, 0x00,
+ 0x8C, 0x79, 0x08, 0xB9, 0x80, 0x08, 0xAA, 0xAD,
+ 0x09, 0x07, 0xD0, 0x0B, 0xAD, 0x0A, 0x07, 0xD0,
+ 0x03, 0xCE, 0x0B, 0x07, 0xCE, 0x0A, 0x07, 0xCE,
+ 0x09, 0x07, 0xEE, 0x79, 0x08, 0x8A, 0x18, 0x60,
+ 0xA0, 0x01, 0x8C, 0x76, 0x08, 0x38, 0x60, 0xA0,
+ 0x01, 0x8C, 0x7A, 0x08, 0x38, 0x60, 0x00, 0x03,
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
diff --git a/ksiders/makeatr.1 b/ksiders/makeatr.1
new file mode 100644
index 0000000..fc2ab5a
--- /dev/null
+++ b/ksiders/makeatr.1
@@ -0,0 +1,92 @@
+.TH MAKEATR,UNMAKATR "1" "April 2007" "makeatr, unmakatr" "Ken Siders' Atari 8-bit Tools"
+.SH NAME
+\fBmakeatr\fR \- convert an Atari executable to a "K-file" bootable disk
+.br
+\fBunmakatr\fR \- extract original executable from a disk made with \fBmakeatr\fR
+
+.SH SYNOPSIS
+\fBmakeatr\fR \fIimagefile.atr\fR \fIatari-exe\fR
+.br
+\fBunmakeatr\fR \fIimagefile.atr\fR \fIatari-exe\fR
+
+.SH DESCRIPTION
+For various reasons, it's sometimes useful to make a bootable disk image
+of an Atari executable. \fBmakeatr\fR does exactly that.
+.PP
+If you have a bootable image made with \fBmakeatr\fR, but you've lost the
+original executable, you can extract it from the image with \fBunmakatr\fR.
+.PP
+.SH NOTES
+\fBmakeatr\fR is only useful for single\-file games or other programs that
+don't need DOS disk access to run: the bootable image won't contain
+any DOS at all, or even a DOS file structure (you can't read the directory).
+.PP
+\fBunmakatr\fR only works for images created by \fBmakeatr\fR. It is not
+a general-purpose bootdisk-to-executable utility.
+.PP
+Disks made with \fBmakeatr\fR are recognizable when booted on the Atari,
+because they display GR.0 screen with a lowercase \fIk\fR
+(screen is otherwise blank).
+.PP
+Disk images made with \fBmakeatr\fR are only created with enough sectors
+to hold the executable. AtariSIO can handle these images fine, as can
+most other ATR utilities, but the smaller-than-regulation size may cause
+problems with some utilities.
+.PP
+There is only one Atari executable format, regardless of filename extension.
+All .XEX, .COM, .BIN, .EXE, etc. files for the Atari are the same file type,
+regardless of the name, and regardless of what you may have read on some
+forum site. Atari DOS doesn't care about the filename; it only exists for
+human consumption (and for filetype association with emulators, these
+days).
+
+.SH DIAGNOSTICS
+\fBmakeatr\fR's error messages are not well documented. They consist only
+of an internal error number code, listed here:
+.PP
+.B 1
+\- Unable to open ATR image for writing
+.br
+.B 2
+\- Unable to write image ATR header
+.br
+.B 6
+\- Unable to write boot sectors to ATR image
+.br
+.B 7
+\- Error while padding ATR image to an even sector size
+.br
+.B 11
+\- Unable to stat the input file (not found, permission denied, etc)
+.br
+.B 12
+\- Input file is 0 bytes in length
+.br
+.B 13
+\- Can't open input file
+.br
+.B 19
+\- Error writing to ATR image, \fBor\fR trailing junk in the input image
+.br
+.B 20
+\- Input file is not an Atari executable (missing 0xFF 0xFF signature)
+.br
+.PP
+\fBunmakatr\fR reports "Error #0" for any and all errors it may encounter.
+.PP
+Both \fBmakeatr\fR and \fBunmakatr\fR return a 0 exit status to the caller
+for success, or non-zero status for failure (and print one of the above
+error messages, as well). Error messages are printed to standard \fBoutput\fR,
+not standard error output.
+
+.SH AUTHOR
+Ken Siders <\fBatari8bit@columbus.rr.com\fR>
+.PP
+Man page by B. Watson <\fBurchlay@urchlay.com\fR>
+
+.SH SEE ALSO
+\&\fIatrdir\fR\|(1), \&\fIatrextr\fR\|(1), \&\fIsortatr\fR\|(1).
+.PP
+Ken Siders Atari 8-bit page:
+.br
+http://atari.ksiders.tzo.com/a8emulators.html
diff --git a/ksiders/makeatr.c b/ksiders/makeatr.c
new file mode 100644
index 0000000..457e0e5
--- /dev/null
+++ b/ksiders/makeatr.c
@@ -0,0 +1,41 @@
+/* Copyright 1997 Ken Siders */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "atr.h"
+#include "atdos.h"
+
+
+/********************************************************************
+ Testing
+********************************************************************/
+
+
+int main( int argc, char **argv)
+ {
+ int stat = 0;
+
+ printf("MakeAtr Version 0.9u (c)1997 Ken Siders\n");
+ printf("Ported and modified by B. Watson, 2007\n");
+ printf("This program may be freely distributed\n\n");
+
+
+ if (argc != 3)
+ {
+ printf("usage: makeatr atrname.atr file\n\n");
+ }
+ else
+ {
+ stat = CreateBootAtr( argv[1], argv[2] );
+ if ( stat )
+ printf("Error #%d encountered in conversion\n\n", stat);
+ else
+ printf("No errors, %s created successfully\n\n", argv[1]);
+ }
+
+ exit(stat);
+
+}
+
+
diff --git a/ksiders/sortatr.1 b/ksiders/sortatr.1
new file mode 100644
index 0000000..98301b3
--- /dev/null
+++ b/ksiders/sortatr.1
@@ -0,0 +1,80 @@
+.TH SORTATR "1" "April 2007" "sortatr" "Ken Siders' Atari 8-bit Tools"
+.SH NAME
+\fBsortatr\fR \- sort Atari DOS directory in an ATR image
+
+.SH SYNOPSIS
+\fBsortatr\fR \fIimagefile.atr\fR
+
+.SH DESCRIPTION
+\fBsortatr\fR sorts the directory entries in an Atari DOS 2.0S or MyDOS
+compatible ATR images. The sort is alphabetical, and the disk image
+is modified in place.
+.PP
+\fBatrextr\fR does not take any options.
+.SH WARNINGS
+\fBDO NOT USE\fR this utility with DOS 2.5 disk images!
+.PP
+\fBDO NOT USE\fR this utility with MyDOS images containing subdirectories!
+.PP
+\fBDATA LOSS\fR will occur if these warnings are not heeded! The modified
+directory in the image will not contain any references to DOS 2.5 files
+using sectors above 720, or to MyDOS subdirectories. This effectively
+\fBdeletes\fR these files from the image.
+.PP
+If in doubt, list the image's directory with \fBatrdir\fR. If you see
+files marked with a \fI:\fR (colon) or \fI<\fR (less-than), then
+\fBsortatr\fR should not be used on the image. Even if your purpose
+is actually to delete these files/dirs, you should use some other
+utility (or DOS on a real or emulated Atari) to delete them, because
+\fBsortatr\fR doesn't free the sectors occupied by the files/dirs.
+.PP
+If you ignore these warnings and manage to delete files or directories
+from your disk image, it should be possible to recover them with a good
+Atari undelete or disk\-repair utility. Although the affected files/dirs
+have been removed from the disk directory, the data sectors have not
+been overwritten nor marked as free (meaning they won't be overwritten
+by later write operations, unlike most "undelete" situations).
+First, you have to
+find the starting sector of the missing file/dir and
+create a directory entry for it in the directory at sectors 36-368. You
+will probably also have to change the file number in the sector link
+bytes, or else delete the file in the new directory that's using the
+file number of the old (deleted) file.
+.PP
+Further warnings:
+.PP
+\fBDO NOT USE\fR this utility with any non-standard (non\-AtariDOS,
+non-MyDOS compatible) disk image.
+.PP
+Boot disks with no directory,
+SpartaDOS, Atari DOS 3/4, DOS XE, or other non\-standard DOS formatted
+disks might (if you're lucky) just cause an error message. If
+you're not lucky, they may be corrupted. There is no easy way to
+recover an image that gets damaged in this way.
+.PP
+\fBDO NOT USE\fR this utility with \fIany\fR image without creating a
+backup first!
+.PP
+Any image you use with \fBsortatr\fR is modified in-place,
+and no backup is made by \fBsortatr\fR. \fBAlways\fR make a backup
+copy of an ATR image before you use \fBsortatr\fR on it. \fIYou have
+been warned\fI.
+.SH NOTES
+XFD format images are not supported.
+.PP
+Exit status is zero if the sort was successful, or
+non-zero if any errors occurred. In case of a non-zero return,
+the disk image may be truncated, or its directory sectors may be
+damaged. Hope you had a backup of that image!
+.SH AUTHOR
+Ken Siders <\fBatari8bit@columbus.rr.com\fR>
+.PP
+Man page by B. Watson <\fBurchlay@urchlay.com\fR>
+
+.SH SEE ALSO
+\&\fIatrdir\fR\|(1), \&\fIatrextr\fR\|(1), \&\fImakeatr\fR\|(1),
+\&\fIunmakatr\fR\|(1).
+.PP
+Ken Siders Atari 8-bit page:
+.br
+http://atari.ksiders.tzo.com/a8emulators.html
diff --git a/ksiders/sortatr.c b/ksiders/sortatr.c
new file mode 100644
index 0000000..394dc28
--- /dev/null
+++ b/ksiders/sortatr.c
@@ -0,0 +1,39 @@
+/* Copyright 1997 Ken Siders */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "atr.h"
+#include "atdos.h"
+
+/* SortAtr */
+
+
+int main( int argc, char **argv)
+ {
+ int stat;
+
+ printf("SortATR v0.9 (C)1997 Ken Siders\n");
+ printf("This program may be freely distributed\n\n");
+
+ if ( argc != 2 )
+ {
+ printf("usage: sortatr atrname\n");
+ exit(2);
+ }
+ stat = SortAtariDir( argv[1] );
+ if ( stat )
+ {
+ printf("Error sorting directory\n");
+ exit(3);
+ }
+ else
+ {
+ printf("no errors\n");
+ exit(0);
+ }
+
+
+}
+
+
diff --git a/ksiders/unmakatr.c b/ksiders/unmakatr.c
new file mode 100644
index 0000000..c50b55d
--- /dev/null
+++ b/ksiders/unmakatr.c
@@ -0,0 +1,43 @@
+/* Copyright 1997 Ken Siders */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "atr.h"
+#include "atdos.h"
+
+
+/********************************************************************
+ Testing
+********************************************************************/
+
+
+int main( int argc, char **argv)
+ {
+ long size = 0;
+
+ printf("MakeAtr Version 0.9u (c)1997 Ken Siders\n");
+ printf("Ported and modified by B. Watson, 2007\n");
+ printf("This program may be freely distributed\n\n");
+
+
+ if (argc != 3)
+ {
+ printf("usage: unmakatr atrname.atr file\n\n");
+ }
+ else
+ {
+ size = ExtractExeFromBootAtr( argv[1], argv[2] );
+ if ( size <= 0 ) {
+ printf("Error #%ld encountered in extracting\n\n", -size);
+ exit(size);
+ } else {
+ printf("No errors, %s extracted successfully (%ld bytes)\n\n", argv[2], size);
+ }
+ }
+
+ exit(0);
+
+}
+
+