diff --git a/Makefile b/Makefile index 47c29e7..2e8daf8 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,7 @@ SOURCES_lsatr=\ lsatr.c\ lssfs.c\ lsdos.c\ + lsextra.c\ msg.c\ CFLAGS=-O2 -Wall diff --git a/src/lsatr.c b/src/lsatr.c index d522e56..9c1aa86 100644 --- a/src/lsatr.c +++ b/src/lsatr.c @@ -20,6 +20,7 @@ #include "atr.h" #include "compat.h" #include "lsdos.h" +#include "lsextra.h" #include "lssfs.h" #include "msg.h" #include @@ -113,11 +114,11 @@ int main(int argc, char **argv) int e = sfs_read(atr, atr_name, atari_list, lower_case, extract_files); if( e ) - { e = dos_read(atr, atr_name, atari_list, lower_case, extract_files); - if( e ) - show_msg("%s: ATR image format not supported.", atr_name); - } + if( e ) + e = extra_read(atr, atr_name, atari_list, lower_case, extract_files); + if( e ) + show_msg("%s: ATR image format not supported.", atr_name); atr_free(atr); return e; diff --git a/src/lsextra.c b/src/lsextra.c new file mode 100644 index 0000000..b46960a --- /dev/null +++ b/src/lsextra.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2023 Daniel Serpell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see + */ +/* + * Extracts various simple boot formats. + */ +#include "lsextra.h" +#include "msg.h" +#include +#include +#include +#include +#include +#include +#include + +static unsigned get_name(char *name, char *aname, const uint8_t *data, int max, + int lower_case) +{ + unsigned l = 0; + unsigned a = 0; + int dot = 0; + memset(aname, ' ', max + 1); + aname[max + 1] = 0; + for( int i = 0; i < max; i++ ) + { + uint8_t c = data[i]; + if( c >= 'A' && c <= 'Z' && lower_case ) + c = c - 'A' + 'a'; + if( c == '.' ) + { + // Store dot + dot = 1; + name[l++] = c; + while( a < 8 ) + aname[a++] = ' '; + continue; + } + if( c < ' ' || c == '/' || c == '.' || c == '?' || c == '\\' || c == 96 || + c > 'z' ) + c = '_'; + else if( c == ' ' ) + continue; + if( i > 7 && !dot ) + { + dot = 1; + name[l++] = '.'; + aname[a++] = ' '; + } + name[l++] = c; + aname[a++] = c; + } + name[l] = 0; + return l; +} + +static uint16_t read16(const uint8_t *p) +{ + return (p == NULL) ? 0 : (p[0] | (p[1] << 8)); +} + +static int check_bas2boot(struct atr_image *atr) +{ + // BAS2BOOT only supports 128 bytes per sector, and we must have + // at least one data sector + if( atr->sec_size != 128 || atr->sec_count < 2 ) + return 0; + + const uint8_t *sec1 = atr_data(atr, 1); + const uint8_t *sec2 = atr_data(atr, 2); + + // Check boot count == 2 and load address == $700 + if( read16(sec1) != 0x200 || read16(sec1 + 2) != 0x700 ) + return 0; + + // Check ID + if( memcmp(sec2 + 0x3C, "BAS2BOOT", 8) ) + return 0; + + // Ok, we have a BAS2BOOT file + return 1; +} + +static void extract_bas2boot(struct atr_image *atr, int atari_list, int lower_case, + int extract_files) +{ + // Get headers + const uint8_t *sec1 = atr_data(atr, 1); + const uint8_t *sec2 = atr_data(atr, 2); + // Get filename + char path[32], aname[32]; + if( !get_name(path, aname, sec2 + 0x60, 12, lower_case) || !*path ) + { + strcpy(path, "noname.bas"); + strcpy(aname, "NONAME BAS"); + } + // Adds '.BAS' if no extension is present + if( !strchr(path, '.') ) + { + const char *ext = lower_case ? ".bas" : ".BAS"; + strcat(path, ext); + memcpy(aname + 9, ext + 1, 3); + } + + // Get data and length + unsigned fsize = read16(sec1 + 8); + uint8_t *fdata = check_malloc(fsize < 16 ? 16 : fsize); + + // Read header and undo bad conversion for certain files + memcpy(fdata, sec2 + 0x72, 14); + for( int i = 2; i < 14; i += 2 ) + { + int x = fdata[0] + fdata[1] * 256; + int y = fdata[i] + fdata[i + 1] * 256 + x; + fdata[i] = y & 0xFF; + fdata[i + 1] = y >> 8; + } + // Read rest of file + unsigned secnum = 3; + for( unsigned pos = 14; pos < fsize; pos += 128, secnum++ ) + { + unsigned len = fsize - pos < 128 ? fsize - pos : 128; + const uint8_t *s = atr_data(atr, secnum); + if( !s ) + memset(fdata + pos, 0, len); + else + memcpy(fdata + pos, s, len); + } + + if( extract_files ) + { + struct stat st; + fprintf(stderr, "%s\n", path); + // Check if file already exists: + if( 0 == stat(path, &st) ) + show_error("%s: file already exists.", path); + // Create new file + int fd = creat(path, 0666); + if( fd == -1 ) + show_error("%s: can´t create file, %s", path, strerror(errno)); + if( fsize != write(fd, fdata, fsize) ) + show_error("%s: can´t write file, %s", path, strerror(errno)); + if( close(fd) ) + show_error("%s: can´t write file, %s", path, strerror(errno)); + } + else if( atari_list ) + printf("%-12s %7u\n", aname, fsize); + else + printf("%8u\t\t%s\n", fsize, path); + + free(fdata); +} + +int extra_read(struct atr_image *atr, const char *atr_name, int atari_list, + int lower_case, int extract_files) +{ + // Check BAS2BOOT + if( check_bas2boot(atr) ) + { + if( atari_list ) + printf("ATR image: %s\n" + "Image size: %u sectors of %u bytes\n" + "Volume: BAS2BOOT\n", + atr_name, atr->sec_count, atr->sec_size); + else + printf("%s: %u sectors of %u bytes, BAS2BOOT.\n", atr_name, atr->sec_count, + atr->sec_size); + extract_bas2boot(atr, atari_list, lower_case, extract_files); + return 0; + } + + return 1; +} diff --git a/src/lsextra.h b/src/lsextra.h new file mode 100644 index 0000000..8a93024 --- /dev/null +++ b/src/lsextra.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 Daniel Serpell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see + */ +/* + * Extracts various simple boot formats. + */ +#pragma once +#include "atr.h" + +int extra_read(struct atr_image *atr, const char *atr_name, int atari_list, + int lower_case, int extract_files);