diff options
author | B. Watson <urchlay@slackware.uk> | 2024-05-10 17:24:40 -0400 |
---|---|---|
committer | B. Watson <urchlay@slackware.uk> | 2024-05-10 17:24:40 -0400 |
commit | f9dcbdd176785dfc9d49f3113ec6110199e9a246 (patch) | |
tree | f132ba50240e442f3c669bdbbfe014843db47701 /ksiders/atr.c | |
parent | 516fd094e69c64cecab68ce7a7751c0fa5d868ef (diff) | |
download | bw-atari8-tools-f9dcbdd176785dfc9d49f3113ec6110199e9a246.tar.gz |
import ken siders atr utilities.
Diffstat (limited to 'ksiders/atr.c')
-rw-r--r-- | ksiders/atr.c | 1422 |
1 files changed, 1422 insertions, 0 deletions
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, §orSize, §orCount, &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, §orSize, §orCount, &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, §orSize, §orCount, &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, §orSize, §orCount, &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; + } + + |