diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile | 150 | ||||
| -rw-r--r-- | src/README_Windows.txt.in | 17 | ||||
| -rw-r--r-- | src/addrs.h | 86 | ||||
| -rw-r--r-- | src/alf.1 | 214 | ||||
| -rw-r--r-- | src/alf.c | 441 | ||||
| -rw-r--r-- | src/alf.rst | 186 | ||||
| -rw-r--r-- | src/alfsum.1 | 107 | ||||
| -rw-r--r-- | src/alfsum.c | 54 | ||||
| -rw-r--r-- | src/alfsum.rst | 85 | ||||
| -rw-r--r-- | src/alfusage.c | 11 | ||||
| -rw-r--r-- | src/asmcode.c | 650 | ||||
| -rw-r--r-- | src/extract.c | 197 | ||||
| -rw-r--r-- | src/glob.c | 115 | ||||
| -rw-r--r-- | src/io.c | 163 | ||||
| -rw-r--r-- | src/listalf.c | 125 | ||||
| -rw-r--r-- | src/manftr.rst | 8 | ||||
| -rw-r--r-- | src/mkusage.pl | 19 | ||||
| -rw-r--r-- | src/opts.c | 78 | ||||
| -rw-r--r-- | src/self.c | 13 | ||||
| -rw-r--r-- | src/self.h | 3 | ||||
| -rw-r--r-- | src/unalf.1 | 412 | ||||
| -rw-r--r-- | src/unalf.c | 79 | ||||
| -rw-r--r-- | src/unalf.h | 93 | ||||
| -rw-r--r-- | src/unalf.rst | 358 | ||||
| -rw-r--r-- | src/usage.c | 19 | ||||
| -rw-r--r-- | src/ver.rst | 1 |
26 files changed, 0 insertions, 3684 deletions
diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index da19876..0000000 --- a/src/Makefile +++ /dev/null @@ -1,150 +0,0 @@ -# Makefile for alftools, by B. Watson. WTFPL. -# Tested with GNU make, BSD make, makepp, and Schily's dmake. - -### Override these variables as needed. Don't override CFLAGS; use -# COPT instead. - -# C compiler to use. PATH is searched unless you give an absolute path. -CC=cc - -# C compiler options (flags): -COPT=-O3 - -# Path to the GNU or BSD 'install' program. -INSTALL=install - -# Path to the rst2man command. Only needed if modifying/rebuilding the -# man pages and/or windows HTML docs. -RST2MAN=rst2man - -# These only affect the 'install' target (they don't get compiled -# into the binary). - -PREFIX=/usr -MANDIR=$(PREFIX)/man -MAN1DIR=$(MANDIR)/man1 -BINDIR=$(PREFIX)/bin -DOCDIR=$(PREFIX)/doc/$(PROJ)-$(VERSION) -DESTDIR= -INSTALL_DATA=$(INSTALL) -m0644 -INSTALL_PROGRAM=$(INSTALL) -m0755 -GZIP=gzip -9 -GZIP_MAN=yes -STRIP=yes - -# -### No user-serviceable parts below. - -PROJ=alftools -VERSION=0.2.0 - -CFLAGS=-DVERSION='"$(VERSION)"' -Wall -I../f65 $(COPT) - -BINS=alf alfsum unalf -MANS=alf.1 alfsum.1 unalf.1 - -UNALF_OBJS=unalf.o io.o listalf.o extract.o f65.o glob.o opts.o usage.o self.o asmcode.o -ALFSUM_OBJS=alfsum.o self.o -ALF_OBJS=alf.o self.o alfusage.o - -.PHONY: all clean install crosswin windows windows-upload realclean - -all: $(BINS) $(MANS) - -# unalf and alfsum need explicit rules for BSD make compatibility. -# without them, bmake tries to run "cc -o unalf unalf.c" which ain't -# gonna ever work. -unalf: $(UNALF_OBJS) - $(CC) $(LDFLAGS) -o $@ $(UNALF_OBJS) - -alfsum: $(ALFSUM_OBJS) - $(CC) $(LDFLAGS) -o $@ $(ALFSUM_OBJS) - -alf: $(ALF_OBJS) - $(CC) $(LDFLAGS) -o $@ $(ALF_OBJS) - -usage.o: usage.c - -alfusage.o: usage.c - -f65.o: ../f65/f65.c ../f65/f65.h - $(CC) $(CFLAGS) -c -o f65.o ../f65/f65.c - -usage.c: mkusage.pl unalf.rst - perl mkusage.pl unalf.rst > usage.c - -alfusage.c: mkusage.pl alf.rst - perl mkusage.pl alf.rst > alfusage.c - -unalf.o: unalf.c unalf.h ../f65/f65.h - -glob.o: glob.c unalf.h - -opts.o: opts.c unalf.h - -io.o: io.c unalf.h addrs.h ../f65/f65.h - -listalf.o: listalf.c addrs.h unalf.h ../f65/f65.h - -extract.o: extract.c addrs.h unalf.h ../f65/f65.h - -ver.rst: - echo '.. |version| replace:: $(VERSION)' > ver.rst - -unalf.1: unalf.rst ver.rst manftr.rst - $(RST2MAN) unalf.rst > unalf.1 - -alfsum.1: alfsum.rst ver.rst manftr.rst - $(RST2MAN) alfsum.rst > alfsum.1 - -alf.1: alf.rst ver.rst manftr.rst - $(RST2MAN) alf.rst > alf.1 - -clean: - rm -rf *.o $(BINS) *.exe wintmp \ - alf.html unalf.html alfsum.html *.aarch64.elf *.com.dbg - -# these are generated files, but they *are* checked into git. -realclean: clean - rm -f unalf.1 alfsum.1 usage.c alfusage.c - -install: all - [ "$(STRIP)" = "yes" ] && strip alf unalf alfsum || true - mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MAN1DIR) $(DESTDIR)$(DOCDIR) - $(INSTALL_DATA) alf.1 $(DESTDIR)$(MAN1DIR) - $(INSTALL_DATA) unalf.1 $(DESTDIR)$(MAN1DIR) - $(INSTALL_DATA) alfsum.1 $(DESTDIR)$(MAN1DIR) - [ "$(GZIP_MAN)" = "yes" ] && $(GZIP) $(DESTDIR)$(MAN1DIR)/alf.1 || true - [ "$(GZIP_MAN)" = "yes" ] && $(GZIP) $(DESTDIR)$(MAN1DIR)/unalf.1 || true - [ "$(GZIP_MAN)" = "yes" ] && $(GZIP) $(DESTDIR)$(MAN1DIR)/alfsum.1 || true - $(INSTALL_PROGRAM) alf $(DESTDIR)$(BINDIR) - $(INSTALL_PROGRAM) unalf $(DESTDIR)$(BINDIR) - $(INSTALL_PROGRAM) alfsum $(DESTDIR)$(BINDIR) - cp -r ../README.txt ../LICENSE.txt ../TODO.txt ../doc ../examples $(DESTDIR)$(DOCDIR) - -crosswin: clean - $(MAKE) CC=~/x-tools/x86_64-w64-mingw32/bin/x86_64-w64-mingw32-gcc - -html: alfsum.html unalf.html alf.html - -alf.html: alf.rst - rst2html alf.rst > alf.html - -alfsum.html: alfsum.rst - rst2html alfsum.rst > alfsum.html - -unalf.html: unalf.rst - rst2html unalf.rst > unalf.html - -windows: crosswin html - rm -rf wintmp ../$(PROJ)-$(VERSION)-win64.zip - mkdir -p wintmp/{doc,examples} - cp alf.exe unalf.exe alfsum.exe wintmp - cd .. ; for i in README.txt TODO.txt doc/*.txt doc/Arcinfo examples/*.txt; do perl -pe 's,\n,\r\n,;' $$i > src/wintmp/$$i; done - perl -p -e 's,\n,\r\n,;' -e 's,\@VERSION\@,$(VERSION),g;' README_Windows.txt.in > wintmp/README_Windows.txt - cp ../examples/*.alf wintmp/examples - cp alfsum.html unalf.html alf.html wintmp - cd wintmp ; zip -r ../../$(PROJ)-$(VERSION)-win64.zip * - -windows-upload: windows - scp ../$(PROJ)-$(VERSION)-win64.zip sluk:public_html/html/$(PROJ)4win/ diff --git a/src/README_Windows.txt.in b/src/README_Windows.txt.in deleted file mode 100644 index 338a495..0000000 --- a/src/README_Windows.txt.in +++ /dev/null @@ -1,17 +0,0 @@ -This is the Windows binary release of alftools-@VERSION@. - -See README.txt, unalf.html, and alfsum.html for full documentation. - -This is a command-line program. Just double-clicking on the .exe won't -do anything useful. You should copy unalf.exe and alfsum.exe to some -directory that's in your PATH, or else run them from the directory you -extracted them to, using the command prompt (CMD.EXE). - -The binaries (alf.exe, unalf.exe, alfsum.exe) were built with a cross -gcc compiler on a Linux system. They should run on any recent 64-bit -Windows OS. The author doesn't actually use Windows, so if you run -into problems, please let me know. - -I can be reached by email at: - -urchlay@slackware.uk diff --git a/src/addrs.h b/src/addrs.h deleted file mode 100644 index 47e0e32..0000000 --- a/src/addrs.h +++ /dev/null @@ -1,86 +0,0 @@ - -#define buf_adr_l 0x7A7B -#define buf_adr_h 0x7A7C -#define buf_len_l 0x7A7D -#define buf_len_h 0x7A7E -#define zp_b0 0x00B0 -#define zp_b1 0x00B1 -#define acc16_l 0x00B2 -#define acc16_h 0x00B3 -#define zp_b4 0x00B4 -#define zp_b5 0x00B5 -#define stackptr_l 0x00B6 -#define stackptr_h 0x00B7 -#define zp_b8 0x00B8 -#define zp_b9 0x00B9 -#define outbuf_ptr_l 0x00BA -#define outbuf_ptr_h 0x00BB -#define zp_bc 0x00BC -#define zp_bd 0x00BD -#define zp_be 0x00BE -#define zp_bf 0x00BF -#define MEMTOP 0x02E5 -#define MEMTOP_lo 0x02E5 -#define MEMTOP_hi 0x02E6 -#define MEMLO 0x02E7 -#define MEMLO_hi 0x02E8 -#define input_file 0x7000 -#define output_dir 0x7050 -#define outfile_l 0x70A0 -#define outfile_h 0x70A1 -#define linbuf 0x7120 -#define L7174 0x7174 -#define L7175 0x7175 -#define L7176 0x7176 -#define L7177 0x7177 -#define L7178 0x7178 -#define L7179 0x7179 -#define L717A 0x717A -#define L717B 0x717B -#define L717C 0x717C -#define L717D 0x717D -#define L717E 0x717E -#define cksum_l 0x717F -#define cksum_h 0x7180 -#define L7181 0x7181 -#define L7182 0x7182 -#define inbuf_len_l 0x7183 -#define inbuf_len_h 0x7184 -#define inbuf_adr_l 0x7185 -#define inbuf_adr_h 0x7186 -#define outbuf_adr_l 0x7187 -#define outbuf_adr_h 0x7188 -#define L7189 0x7189 -#define L718A 0x718A -#define L718B 0x718B -#define L718C 0x718C -#define alf_header 0x718D -#define alf_hdr_sig 0x718E -#define alf_hdr_filename 0x718F -#define alf_hdr_compsize0 0x719C -#define alf_hdr_compsize1 0x719D -#define alf_hdr_compsize2 0x719E -#define alf_hdr_compsize3 0x719F -#define alf_hdr_date0 0x71A0 -#define alf_hdr_date1 0x71A1 -#define alf_hdr_time0 0x71A2 -#define alf_hdr_time1 0x71A3 -#define alf_hdr_cksum_l 0x71A4 -#define alf_hdr_cksum_h 0x71A5 -#define alf_hdr_origsize0 0x71A6 -#define alf_hdr_origsize1 0x71A7 -#define alf_hdr_origsize2 0x71A8 -#define alf_hdr_origsize3 0x71A9 -#define L71AA 0x71AA -#define L71AB 0x71AB -#define shift_counter 0x71AC -#define L71AD 0x71AD -#define L71AE 0x71AE -#define L71AF 0x71AF -#define L71B0 0x71B0 -#define outbuf_len_l 0x71B1 -#define outbuf_len_h 0x71B2 -#define L71B4 0x71B4 -#define L71B5 0x71B5 -#define L71B6 0x71B6 -#define L71B7 0x71B7 diff --git a/src/alf.1 b/src/alf.1 deleted file mode 100644 index f2efb38..0000000 --- a/src/alf.1 +++ /dev/null @@ -1,214 +0,0 @@ -.\" Man page generated from reStructuredText. -. -. -.nr rst2man-indent-level 0 -. -.de1 rstReportMargin -\\$1 \\n[an-margin] -level \\n[rst2man-indent-level] -level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] -- -\\n[rst2man-indent0] -\\n[rst2man-indent1] -\\n[rst2man-indent2] -.. -.de1 INDENT -.\" .rstReportMargin pre: -. RS \\$1 -. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] -. nr rst2man-indent-level +1 -.\" .rstReportMargin post: -.. -.de UNINDENT -. RE -.\" indent \\n[an-margin] -.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] -.nr rst2man-indent-level -1 -.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] -.in \\n[rst2man-indent\\n[rst2man-indent-level]]u -.. -.TH "ALF" 1 "2025-11-27" "0.2.0" "Urchlay's Atari 8-bit Tools" -.SH NAME -alf \- create Atari 8-bit ALF archives -.\" RST source for alf(1) man page. Convert with: -. -.\" rst2man.py alf.rst > alf.1 -. -.SH SYNOPSIS -.sp -alf [\fB\-\-help\fP] | [\fB\-a\fP | \fB\-o\fP ] [\fB\-t[dgz]\fP] \fIalf\-file\fP \fIfile\fP [\fIfile\fP ...] -.SH DESCRIPTION -.sp -\fBalf\fP creates \fIALF\fP archives. -.sp -\fIALF\fP is a compressed archive format similar to \fBarc\fP(1), though -not compatible with it. It was used on the Atari 8\-bit platform -beginning in the late 1980s. The Atari executables are \fBLZ.COM\fP -for the compressor and \fBDZ.COM\fP for the decompressor. \fBalf\fP is a -rewrite of \fBLZ.COM\fP for modern operating systems, with added features. -.sp -By default, the \fIalf\-file\fP is created, and the \fIfile\fPs are compressed -and added to it. If \fIalf\-file\fP already existed, it is backed up by adding -a \fB~\fP to the filename. -.sp -The \fBALF\fP file format only supports uppercase "8.3" filenames -(Atari DOS or MS\-DOS style), and does not support subdirectories. The -filenames you give will be stored in the \fBALF\fP file uppercased -and truncated to fit the 8.3 scheme. This could result in duplicate -filenames in the archive, if you\(aqre not careful. -.SH OPTIONS -.INDENT 0.0 -.TP -.B \-a -Append to \fIalf\-file\fP\&. The files are added to the end of the archive. Be -careful not to add a file with the same name as as existing member of -the archive. If \fIalf\-file\fP doesn\(aqt exist, it will be created. No backup -is made with \fB\-a\fP\&. -.UNINDENT -.\" append to alf file. -. -.INDENT 0.0 -.TP -.B \-A -Convert line endings and tabs from ASCII to ATASCII in all input -files. This will corrupt any executables or non\-text data files, -so use with caution. There is no way to autodetect text files, -unlike \fBunalf\fP\&. -.UNINDENT -.\" convert EOLs and tabs in all input files. -. -.INDENT 0.0 -.TP -.B \-h\fP,\fB \-\-help -Show built\-in help message. -.UNINDENT -.\" show this help message. -. -.INDENT 0.0 -.TP -.B \-o -Overwrite \fIalf\-file\fP if it exists; do not make a \fB~\fP backup. -.UNINDENT -.\" overwrite alf file if exists (do not create file~ backup). -. -.INDENT 0.0 -.TP -.B \fB\-td\fP -Use default \fBLZ.COM\fP timestamps (8 Dec 82 12:24). -.UNINDENT -.\" use default LZ.COM timestamps. -. -.INDENT 0.0 -.TP -.B \fB\-tu\fP -Use UTC for timestamps (default is local timezone). -.UNINDENT -.\" use UTC timestamps. -. -.INDENT 0.0 -.TP -.B \fB\-tz\fP -Use zero for timestamps (0 ??? 80 12:00a). -.UNINDENT -.\" use zero timestamps. -. -.INDENT 0.0 -.TP -.B \-V\fP,\fB \-\-version -Show \fBunalf\fP version number and exit. -.UNINDENT -.\" show version number. -. -.\" ENDOPTS -. -.SH EXIT STATUS -.INDENT 0.0 -.TP -.B 0 -Success. -.TP -.B 1 -Fatal error (I/O or bad command\-line arguments). -.UNINDENT -.SH NOTES -.SS Compatibility -.sp -This \fBalf\fP is \fIintended\fP to be 100% compatible with the original -Atari \fBLZ.COM\fP aka \fBALF.COM\fP, with the following differences: -.INDENT 0.0 -.IP \(bu 2 -There is no interactive mode. The file to create and the files to add -must be given as command\-line arguments. -.IP \(bu 2 -\fBLZ.COM\fP always appends to a file that already exists. This \fBalf\fP -overwrites (making a backup) by default, and can append with the \fB\-a\fP -option. -.IP \(bu 2 -Turning the screen off for speed makes no sense on modern operating -systems, so there\(aqs no option for that. -.UNINDENT -.sp -Note that \fBalf\fP is a complete reverse\-engineered rewrite in C, \fInot\fP -a port of the original 6502 code as \fBunalf\fP is. It\(aqs still being -tested, and may still contain bugs. -.SS File Size Limits -.sp -\fBalf\fP (and \fBLZ.COM\fP) have a 16MB file size limit. \fBuanlf\fP -actually can\(aqt handle files above about 15MB, if you compress one with -\fBalf\fP\&. Real Atari 8\-bit files are never this large anyway, so it\(aqs -a pathological case. A real Atari would take hours or even days to -compress/decompress such files, and you\(aqd have to have a hard disk and -a DOS capable of handling multi\-megabyte files... -.sp -It\(aqs also impossible to compress empty (0\-byte) files. \fBalf\fP will -skip them, if any are found. -.SS Performance -.sp -Performance is \fIhorrible\fP\&. This shouldn\(aqt be a real problem on -modern multi\-GHz CPU, especially since most Atari 8\-bit files are -small (usually under 64KB). Interestingly, it\(aqs not O(n^2), it scales -linearly, O(1): Compressing a 1.3MB text file takes 0.7 seconds on the -author\(aqs (rather modest) Intel i7 workstation, and a file 10x as large -takes approximately 10x as long (7 seconds). A 50KB file is almost -instantaneous, 0.05 seconds, which is more typical of the files you\(aqd -actually use this with. -.SS Timestamps -.sp -The date/time stamps stored in the archive are the \fBmtime\fPs of -the files (which is the same time \fBls\fP(1) shows, by default), and -your local timezone is assumed. Only a 2\-digit year is displayed by -\fBunalf\fP, but the year stored in the \fIALF\fP file doesn\(aqt have a Y2K -problem. It does, however, have a Y2108 problem... -.SH COPYRIGHT -.sp -The original AlfCrunch (\fBDZ.COM\fP and \fBLZ.COM\fP) for the Atari 8\-bit -was released into the Public Domain. \fBalf\fP contains no code from -AlfCrunch. -.sp -\fBalf\fP is released under the WTPFL: Do WTF you want with this. -.SH AUTHOR -.sp -The original AlfCrunch for the Atari 8\-bit was written by Alfred, who -can be reached via the AtariAge.com forums with the username "Alfred". -.sp -This \fBalf\fP was written by B. Watson <\fI\%urchlay@slackware.uk\fP>. -.SH SEE ALSO -.sp -\fBunalf\fP(1), \fBalfls\fP(1) -.sp -The git repository for \fBunalf\fP: -.INDENT 0.0 -.INDENT 3.5 -\fI\%https://slackware.uk/~urchlay/repos/unalf\fP -.UNINDENT -.UNINDENT -.sp -Other Atari\-related projects by the author, including \fBbw\-atari8\-tools\fP -and \fBunmac65\fP: -.INDENT 0.0 -.INDENT 3.5 -\fI\%https://slackware.uk/~urchlay/repos/\fP -.UNINDENT -.UNINDENT -.\" Generated by docutils manpage writer. -. diff --git a/src/alf.c b/src/alf.c deleted file mode 100644 index fb79fcf..0000000 --- a/src/alf.c +++ /dev/null @@ -1,441 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> -#include <unistd.h> -#include <limits.h> -#include <sys/stat.h> -#include <time.h> - -#include "self.h" - -#ifndef u8 -#define u8 unsigned char -#define u16 unsigned short -#endif - -#define INITIAL_BITS 9 -#define MAX_BITS 12 -#define MAX_TOKENS (1 << MAX_BITS) -#define TOK_RESET 256 -#define TOK_END 257 -#define INIT_TOKEN 258 /* 256 = reset dict, 257 = token_bits++ */ - -#define MAX_INPUT_SIZE (1 << 24) - -u8 input_buf[MAX_INPUT_SIZE]; -u8 output_buf[MAX_INPUT_SIZE]; -u8 byte_tokens[256]; -unsigned int input_len, output_len, out_bitpos; - -int opt_append = 0; -int opt_overwrite = 0; -int opt_zerotime = 0; -int opt_alftime = 0; -int opt_gmtime = 0; -int opt_txtconv = 0; - -struct stat in_file_stat; - -typedef struct s_token { - u8 *start; - u16 length; -} token_t; - -token_t tokentab[MAX_TOKENS]; - -FILE *out_file, *in_file; -const char *out_filename, *in_filename; -int token_bits; -int max_token; -int curr_token; -long hdr_compsize_pos; -int in_pos; - -void init_table(void) { - int i; - - memset(tokentab, 0, sizeof(tokentab)); - - token_bits = INITIAL_BITS; - max_token = 1 << INITIAL_BITS; - curr_token = INIT_TOKEN; - - for(i = 0; i < 256; i++) { - byte_tokens[i] = (u8)i; - tokentab[i].start = &byte_tokens[i]; - tokentab[i].length = 1; - } -} - -void store_quad(int pos, unsigned long data) { - int i; - - for(i = 0; i < 4; i++) { - output_buf[pos++] = data & 0xff; - data >>= 8; - } -} - -void store_cksum(void) { - int i; - u16 cksum = 0; - - for(i = 0; i < input_len; i++) - cksum += input_buf[i]; - - output_buf[23] = cksum & 0xff; - output_buf[24] = cksum >> 8; -} - -/* examples: - foo => FOO. - toolongfile => TOOLONGF. (really should be TOOLONGF.ILE) - regular.txt => REGULAR.TXT - too.many.dots => TOO.MAN - */ -void atarify_filename(char *result) { - int i; - char name[9] = { 0 }, ext[4] = { 0 }, *p; - - p = strrchr(in_filename, '/'); - if(p) - p++; - else - p = (char *)in_filename; - - strncpy(name, p, 8); - for(i = 0; i < 8; i++) { - if(!name[i]) break; - if(name[i] == '.') { - name[i] = '\0'; - break; - } - } - - strcpy(result, name); - strcat(result, "."); - - p = strchr(in_filename, '.'); - if(p) { - p++; - strncpy(ext, p, 3); - for(p = ext; *p; p++) - if(*p == '.') *p = 0; - strcat(result, ext); - } - - for(p = result; *p; p++) - *p = toupper(*p); -} - -/* see Arcinfo for the gory details. */ -unsigned long get_msdos_date_time(void) { - time_t t = in_file_stat.st_mtime; - struct tm *tm; - int msdos_year; - u16 ms_date, ms_time; - - if(opt_gmtime) - tm = gmtime(&t); - else - tm = localtime(&t); - - msdos_year = tm->tm_year + 1900 - 1980; - - ms_date = tm->tm_mday | (tm->tm_mon << 5) | (msdos_year << 9); - ms_time = (tm->tm_min << 5) | (tm->tm_hour << 11); - return ms_date | (ms_time << 16); -} - -void create_header(void) { - char hdr_filename[13]; - unsigned long time; - - atarify_filename(hdr_filename); - printf("Crunching %s\n", hdr_filename); - - if(opt_alftime) - time = 0x03130588; - else if(opt_zerotime) - time = 0; - else - time = get_msdos_date_time(); - - output_buf[0] = 0x1a; - output_buf[1] = 0x0f; - memset(&output_buf[3], 0x20, 13); - strncat((char *)&output_buf[2], hdr_filename, 13); - output_buf[14] = 0x00; - store_quad(15, 0); /* compressed size, fill in later */ - store_quad(19, time); - store_cksum(); - store_quad(25, input_len); - output_len = 29; -} - -void update_header(void) { - store_quad(15, output_len - 29); -} - -void open_input(const char *filename) { - in_filename = filename; - if(!(in_file = fopen(in_filename, "rb"))) { - perror(in_filename); - exit(1); - } -} - -void inc_output_len(void) { - if(++output_len == MAX_INPUT_SIZE) { - fprintf(stderr, "%s: fatal: compressed file would be >16MB.\n", self); - exit(1); - } -} - -void append_bit(int bit) { - output_buf[output_len] |= (bit << (7 - out_bitpos)); - out_bitpos++; - if(out_bitpos == 8) { - out_bitpos = 0; - inc_output_len(); - } -} - -void store_token(int tok) { - int mask; - - for(mask = 1 << (token_bits - 1); mask; mask >>= 1) { - append_bit(tok & mask ? 1 : 0); - } -} - -/* match_token() is a brute-force search, which is why alf is so slow. - I'll do something smarter at some point. - search backwards, the tokens are stored with longer ones later - in the list. */ -int match_token(int pos) { - int i, len, maxlen; - token_t *t; - u8 *p, *q; - - maxlen = input_len - pos; - - for(i = curr_token - 1; i >= INIT_TOKEN; i--) { - t = &tokentab[i]; - - /* don't search past the end of the input */ - if(t->length > maxlen) continue; - - /* if the first char doesn't match, don't bother with memcmp. - this is a 5x speedup (!) */ - if(input_buf[pos] != *(t->start)) continue; - - /* this is where alf spends most of its time. - using memcmp is noticeably slower than the code below. */ - /* - if(memcmp(&input_buf[pos], t->start, t->length) == 0) - return i; - */ - - /* inline memcmp replacement of sorts. I don't think it's really - faster than memcmp(), it only seems that way because there's - no function call overhead. ~20% speedup. */ - len = t->length; - p = &input_buf[pos]; - q = t->start; - while(len) { - if(*p != *q) break; - p++; q++; - len--; - } - if(!len) return i; - } - - /* hard-coded single character tokens map to their values, no need - to search. */ - return input_buf[pos]; -} - -void make_token(int start, int end) { - /* if the token table is full, reset it. basically start over like - we would with a new file. */ - if(curr_token == max_token) { - if(token_bits == MAX_BITS) { - store_token(TOK_RESET); /* stored at the *old* token size! */ - token_bits = INITIAL_BITS; - init_table(); - return; /* since we're starting over, *don't* make a token */ - } else { - token_bits++; - } - max_token = 1 << token_bits; - } - tokentab[curr_token].start = &input_buf[start]; - tokentab[curr_token].length = end - start + 1; - curr_token++; -} - -void crunch(void) { - int new_pos; - in_pos = 0; - int token; - - out_bitpos = 0; - - store_token(TOK_RESET); - - while(in_pos < input_len) { - token = match_token(in_pos); - store_token(token); - new_pos = in_pos + tokentab[token].length; - // printf(" in_pos %d, new_pos %d\n", in_pos, new_pos); - if(new_pos < input_len) - make_token(in_pos, new_pos); - in_pos = new_pos; - } - - store_token(TOK_END); - if(out_bitpos) inc_output_len(); - update_header(); -} - -void make_backup(void) { - char bak[PATH_MAX + 2]; - strncpy(bak, out_filename, PATH_MAX); - strcat(bak, "~"); - rename(out_filename, bak); -} - -void convert_eols(void) { - int i; - - for(i = 0; i < input_len; i++) { - if(input_buf[i] == '\n') - input_buf[i] = 0x9b; - else if(input_buf[i] == '\t') - input_buf[i] = 0x7f; - } -} - -void crunch_file(const char *filename) { - init_table(); - - open_input(filename); - - /* read in entire input, couldn't do it this way on the Atari */ - input_len = fread(input_buf, 1, MAX_INPUT_SIZE - 1, in_file); - - if(!feof(in_file)) { - fprintf(stderr, "%s: %s: this file is too large; only compressing the first 16MB.\n", self, filename); - } - - if(!input_len) { - fprintf(stderr, "%s: %s: can't compress a 0-byte (empty) file, skipping.\n", self, filename); - return; - } - - if(opt_txtconv) - convert_eols(); - - output_len = 0; - fstat(fileno(in_file), &in_file_stat); /* for timestamp */ - fclose(in_file); - - memset(output_buf, 0, sizeof(output_buf)); - - create_header(); - - /* crunches the entire input to memory! */ - crunch(); - - /* don't open the output file until crunch() has succeeded once. - this avoids leaving 0-byte turds */ - if(!out_file) { - if(!opt_overwrite) make_backup(); - out_file = fopen(out_filename, opt_append ? "ab" : "wb"); - if(!out_file) { - fprintf(stderr, "%s: fatal: ", self); - perror(out_filename); - exit(1); - } - } - - fwrite(output_buf, 1, output_len, out_file); -} - -void usage(void) { - extern char *usage_msg[]; - char **line; - - puts("alf (ALF compressor) v" VERSION " by B. Watson, WTFPL."); - printf("Usage: %s [-a|-o] archive.alf file [file ...]\n", self); - puts("Options:"); - - for(line = usage_msg; *line; line++) - puts(*line); - - exit(0); -} - -int main(int argc, char **argv) { - int opt; - - set_self(argv[0]); - - if(argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { - usage(); - } - - if(!strcmp(argv[1], "--version")) { - puts(VERSION); - exit(0); - } - - /* don't let getopt() print error message for us. */ - opterr = 0; - - while((opt = getopt(argc, argv, "aAt:oV")) != -1) { - switch(opt) { - case 'A': opt_txtconv = 1; break; - case 'a': opt_append = 1; opt_overwrite = 1; break; - case 'o': opt_overwrite = 1; opt_append = 0; break; - case 't': opt_zerotime = opt_alftime = opt_gmtime = 0; - switch(*optarg) { - case 'z': opt_zerotime = 1; break; - case 'd': opt_alftime = 1; break; - case 'u': opt_gmtime = 1; break; - default: - fprintf(stderr, "%s: fatal: invalid -t suboption '-%c' (try -h or --help)\n", self, *optarg); - exit(1); - } - break; - case 'V': puts(VERSION); exit(0); break; - default: - fprintf(stderr, "%s: fatal: invalid option '-%c' (try -h or --help)\n", self, optopt); - exit(1); - } - } - - if(optind >= argc) { - fprintf(stderr, "%s: fatal: missing alf file argument (try -h or --help)\n", self); - exit(1); - } - - out_filename = argv[optind]; - - optind++; - - if(optind >= argc) { - fprintf(stderr, "%s: fatal: no filenames (nothing to compress) (try -h or --help)\n", self); - exit(1); - } - - while(optind < argc) { - crunch_file(argv[optind++]); - } - - if(out_file) fclose(out_file); - - exit(0); -} diff --git a/src/alf.rst b/src/alf.rst deleted file mode 100644 index 3110180..0000000 --- a/src/alf.rst +++ /dev/null @@ -1,186 +0,0 @@ -.. RST source for alf(1) man page. Convert with: -.. rst2man.py alf.rst > alf.1 - -.. include:: ver.rst - -.. |date| date:: - -=== -alf -=== - -------------------------------- -create Atari 8-bit ALF archives -------------------------------- - -:Manual section: 1 -:Manual group: Urchlay's Atari 8-bit Tools -:Date: |date| -:Version: |version| - -SYNOPSIS -======== - -alf [**--help**] | [**-a** | **-o** ] [**-t[dgz]**] *alf-file* *file* [*file* ...] - -DESCRIPTION -=========== - -**alf** creates *ALF* archives. - -*ALF* is a compressed archive format similar to **arc**\(1), though -not compatible with it. It was used on the Atari 8-bit platform -beginning in the late 1980s. The Atari executables are **LZ.COM** -for the compressor and **DZ.COM** for the decompressor. **alf** is a -rewrite of **LZ.COM** for modern operating systems, with added features. - -By default, the *alf-file* is created, and the *file*\s are compressed -and added to it. If *alf-file* already existed, it is backed up by adding -a **~** to the filename. - -The **ALF** file format only supports uppercase "8.3" filenames -(Atari DOS or MS-DOS style), and does not support subdirectories. The -filenames you give will be stored in the **ALF** file uppercased -and truncated to fit the 8.3 scheme. This could result in duplicate -filenames in the archive, if you're not careful. - -OPTIONS -======= - --a - Append to *alf-file*. The files are added to the end of the archive. Be - careful not to add a file with the same name as as existing member of - the archive. If *alf-file* doesn't exist, it will be created. No backup - is made with **-a**. - -.. append to alf file. - --A - Convert line endings and tabs from ASCII to ATASCII in all input - files. This will corrupt any executables or non-text data files, - so use with caution. There is no way to autodetect text files, - unlike **unalf**. - -.. convert EOLs and tabs in all input files. - --h, --help - Show built-in help message. - -.. show this help message. - --o - Overwrite *alf-file* if it exists; do not make a **~** backup. - -.. overwrite alf file if exists (do not create file~ backup). - -**-td** - Use default **LZ.COM** timestamps (8 Dec 82 12:24). - -.. use default LZ.COM timestamps. - -**-tu** - Use UTC for timestamps (default is local timezone). - -.. use UTC timestamps. - -**-tz** - Use zero for timestamps (0 ??? 80 12:00a). - -.. use zero timestamps. - --V, --version - Show **unalf** version number and exit. - -.. show version number. - -.. ENDOPTS - -EXIT STATUS -=========== - -0 - Success. - -1 - Fatal error (I/O or bad command-line arguments). - -NOTES -===== - -Compatibility -------------- - -This **alf** is *intended* to be 100% compatible with the original -Atari **LZ.COM** aka **ALF.COM**, with the following differences: - -- There is no interactive mode. The file to create and the files to add - must be given as command-line arguments. - -- **LZ.COM** always appends to a file that already exists. This **alf** - overwrites (making a backup) by default, and can append with the **-a** - option. - -- Turning the screen off for speed makes no sense on modern operating - systems, so there's no option for that. - -Note that **alf** is a complete reverse-engineered rewrite in C, *not* -a port of the original 6502 code as **unalf** is. It's still being -tested, and may still contain bugs. - -File Size Limits ----------------- - -**alf** (and **LZ.COM**) have a 16MB file size limit. **uanlf** -actually can't handle files above about 15MB, if you compress one with -**alf**. Real Atari 8-bit files are never this large anyway, so it's -a pathological case. A real Atari would take hours or even days to -compress/decompress such files, and you'd have to have a hard disk and -a DOS capable of handling multi-megabyte files... - -It's also impossible to compress empty (0-byte) files. **alf** will -skip them, if any are found. - -Performance ------------ - -Performance is *horrible*. This shouldn't be a real problem on -modern multi-GHz CPU, especially since most Atari 8-bit files are -small (usually under 64KB). Interestingly, it's not O(n^2), it scales -linearly, O(1): Compressing a 1.3MB text file takes 0.7 seconds on the -author's (rather modest) Intel i7 workstation, and a file 10x as large -takes approximately 10x as long (7 seconds). A 50KB file is almost -instantaneous, 0.05 seconds, which is more typical of the files you'd -actually use this with. - -Timestamps ----------- - -The date/time stamps stored in the archive are the **mtime**\s of -the files (which is the same time **ls**\(1) shows, by default), and -your local timezone is assumed. Only a 2-digit year is displayed by -**unalf**, but the year stored in the *ALF* file doesn't have a Y2K -problem. It does, however, have a Y2108 problem... - -COPYRIGHT -========= - -The original AlfCrunch (**DZ.COM** and **LZ.COM**) for the Atari 8-bit -was released into the Public Domain. **alf** contains no code from -AlfCrunch. - -**alf** is released under the WTPFL: Do WTF you want with this. - -AUTHOR -====== - -The original AlfCrunch for the Atari 8-bit was written by Alfred, who -can be reached via the AtariAge.com forums with the username "Alfred". - -This **alf** was written by B. Watson <urchlay@slackware.uk>. - -SEE ALSO -======== - -**unalf**\(1), **alfls**\(1) - -.. include:: manftr.rst diff --git a/src/alfsum.1 b/src/alfsum.1 deleted file mode 100644 index 6b4ea04..0000000 --- a/src/alfsum.1 +++ /dev/null @@ -1,107 +0,0 @@ -.\" Man page generated from reStructuredText. -. -. -.nr rst2man-indent-level 0 -. -.de1 rstReportMargin -\\$1 \\n[an-margin] -level \\n[rst2man-indent-level] -level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] -- -\\n[rst2man-indent0] -\\n[rst2man-indent1] -\\n[rst2man-indent2] -.. -.de1 INDENT -.\" .rstReportMargin pre: -. RS \\$1 -. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] -. nr rst2man-indent-level +1 -.\" .rstReportMargin post: -.. -.de UNINDENT -. RE -.\" indent \\n[an-margin] -.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] -.nr rst2man-indent-level -1 -.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] -.in \\n[rst2man-indent\\n[rst2man-indent-level]]u -.. -.TH "ALFSUM" 1 "2025-11-26" "0.2.0" "Urchlay's Atari 8-bit Tools" -.SH NAME -alfsum \- calculate ALF checksums -.\" RST source for alfsum(1) man page. Convert with: -. -.\" rst2man.py alfsum.rst > alfsum.1 -. -.SH SYNOPSIS -.sp -alfsum \fBfile\fP [\fBfile\fP ...] | [\fB\-\-help\fP | \fB\-h\fP] | [\fB\-\-version\fP | \fB\-V\fP] -.SH DESCRIPTION -.sp -\fBalfsum\fP calculates the checksums used by the \fBALF\fP compression -utility on the Atari 8\-bit platform. -.sp -To read from standard input, use \fB\-\fP as a filename. -.sp -Output lines have a tab separating the checksum and filename, for ease -of scripting (e.g. with \fBcut\fP(1) or \fBawk\fP(1)). -.sp -\fBalfsum\fP\(aqs primary use is to check files extracted from \fBALF\fP -archives against the checksums stored in the archive. The \fBunalf\fP -program already does this, but \fBalfsum\fP is a separate (much simpler) -program. It serves as a second opinion, in case you suspect a bug in -\fBunalf\fP\&. -.SH OPTIONS -.INDENT 0.0 -.TP -.B \-h\fP,\fB \-\-help -Show built\-in help message. -.TP -.B \-V\fP,\fB \-\-version -Show \fBalfsum\fP version number and exit. -.UNINDENT -.SH NOTES -.sp -The checksum algorithm is very simple: all the bytes in the file are -added together, and the low 16 bits of the result are the checksum. -.SH EXIT STATUS -.INDENT 0.0 -.TP -.B 0 -Success. -.TP -.B 1 to 254 -File I/O error count. If there are more than 254 I/O errors, 254 is returned. -.TP -.B 255 -Error in command\-line arguments: no filenames given. -.UNINDENT -.SH COPYRIGHT -.sp -\fBalfsum\fP is released under the WTPFL: Do WTF you want with this. -.SH AUTHORS -.INDENT 0.0 -.IP B. 3 -Watson <\fI\%urchlay@slackware.uk\fP> -.UNINDENT -.SH SEE ALSO -.sp -\fBalf\fP(1), \fBunalf\fP(1) -.sp -The git repository for \fBunalf\fP: -.INDENT 0.0 -.INDENT 3.5 -\fI\%https://slackware.uk/~urchlay/repos/unalf\fP -.UNINDENT -.UNINDENT -.sp -Other Atari\-related projects by the author, including \fBbw\-atari8\-tools\fP -and \fBunmac65\fP: -.INDENT 0.0 -.INDENT 3.5 -\fI\%https://slackware.uk/~urchlay/repos/\fP -.UNINDENT -.UNINDENT -.\" Generated by docutils manpage writer. -. diff --git a/src/alfsum.c b/src/alfsum.c deleted file mode 100644 index 2456e60..0000000 --- a/src/alfsum.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "unalf.h" - -/* 20251104 bkw: implement the simple checksum used by ALF. - Dumbest possible algo: all the bytes are added together and - the bottom 16 bits of the result is the checksum. */ - -void alfsum(const char *file, FILE *f) { - int c; - unsigned long sum = 0; - - while((c = fgetc(f)) != EOF) - sum += c; - - printf("%04x\t%s\n", (unsigned int)(sum & 0xffff), file); -} - -int main(int argc, char **argv) { - int errs = 0; - char *file; - FILE *f; - - set_self(argv[0]); - - if(argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { - printf("alfsum v" VERSION " by B. Watson. WTFPL.\n" - "Usage: %s filename [filename(s) ...]\n" - "\t(use - to read from standard input)\n", - self); - return (argc < 2) ? -1 : 0; - } - - if(!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) { - puts(VERSION); - exit(0); - } - - while((file = *++argv)) { - if(argv[0][0] == '-' && !argv[0][1]) { - if(isatty(0)) - fprintf(stderr, "%s: reading from stdin...\n", self); - f = stdin; - file = " (stdin)"; - } else if(!(f = fopen(file, "rb"))) { - fprintf(stderr, "%s: ", self); - perror(file); - errs++; - continue; - } - alfsum(file, f); - fclose(f); - } - - return errs > 254 ? 254 : errs; -} diff --git a/src/alfsum.rst b/src/alfsum.rst deleted file mode 100644 index d613626..0000000 --- a/src/alfsum.rst +++ /dev/null @@ -1,85 +0,0 @@ -.. RST source for alfsum(1) man page. Convert with: -.. rst2man.py alfsum.rst > alfsum.1 - -.. include:: ver.rst - -.. |date| date:: - -====== -alfsum -====== - ------------------------ -calculate ALF checksums ------------------------ - -:Manual section: 1 -:Manual group: Urchlay's Atari 8-bit Tools -:Date: |date| -:Version: |version| - -SYNOPSIS -======== - -alfsum **file** [**file** ...] | [**--help** | **-h**] | [**--version** | **-V**] - -DESCRIPTION -=========== - -**alfsum** calculates the checksums used by the **ALF** compression -utility on the Atari 8-bit platform. - -To read from standard input, use **-** as a filename. - -Output lines have a tab separating the checksum and filename, for ease -of scripting (e.g. with **cut**\(1) or **awk**\(1)). - -**alfsum**'s primary use is to check files extracted from **ALF** -archives against the checksums stored in the archive. The **unalf** -program already does this, but **alfsum** is a separate (much simpler) -program. It serves as a second opinion, in case you suspect a bug in -**unalf**. - -OPTIONS -======= - --h, --help - Show built-in help message. - --V, --version - Show **alfsum** version number and exit. - -NOTES -===== - -The checksum algorithm is very simple: all the bytes in the file are -added together, and the low 16 bits of the result are the checksum. - -EXIT STATUS -=========== - -0 - Success. - -1 to 254 - File I/O error count. If there are more than 254 I/O errors, 254 is returned. - -255 - Error in command-line arguments: no filenames given. - -COPYRIGHT -========= - -**alfsum** is released under the WTPFL: Do WTF you want with this. - -AUTHORS -======= - -B. Watson <urchlay@slackware.uk> - -SEE ALSO -======== - -**alf**\(1), **unalf**\(1) - -.. include:: manftr.rst diff --git a/src/alfusage.c b/src/alfusage.c deleted file mode 100644 index 98eb6f2..0000000 --- a/src/alfusage.c +++ /dev/null @@ -1,11 +0,0 @@ -const char *usage_msg[] = { - " -a: append to alf file.", - " -A: convert EOLs and tabs in all input files.", - " -h: show this help message.", - " -o: overwrite alf file if exists (do not create file~ backup).", - " -td: use default LZ.COM timestamps.", - " -tu: use UTC timestamps.", - " -tz: use zero timestamps.", - " -V: show version number.", - (const char*)0 -}; diff --git a/src/asmcode.c b/src/asmcode.c deleted file mode 100644 index 831ba1c..0000000 --- a/src/asmcode.c +++ /dev/null @@ -1,650 +0,0 @@ -#include "unalf.h" -#include "addrs.h" - -extern void chksum_err(void); -extern void truncated_err(void); -extern void stack_underrun(void); -extern void stack_overrun(void); - -void uncrunch_file(void) { - lda_i(0x00); - sta(L71AF); - sta(L71B0); - sta(outbuf_len_l); - sta(outbuf_len_h); - sta(L71B6); - sta(L71B7); - sta(L718C); - sta(cksum_l); - sta(cksum_h); - lda_i(0x09); - sta(shift_counter); - lda_i(0x00); - sta(L71AD); - lda_i(0x02); - sta(L71AE); - lda_i(0x00); - sta(stackptr_l); - lda_i(0x60); - sta(stackptr_h); - jsr(setup_io_bufs); - ldx_i(0x10); - lda(inbuf_adr_l); - sta(buf_adr_l); - lda(inbuf_adr_h); - sta(buf_adr_h); - lda(inbuf_len_l); - sta(buf_len_l); - lda(inbuf_len_h); - sta(buf_len_h); - jsr(L7A19); - sty(L718C); - jsr(L79E7); - lda(buf_len_l); - ora(buf_len_h); - bne(L75B1); - rts(); - -L75B1: - jsr(L76D0); - lda(acc16_h); - cmp_i(0x01); - bne(uncrunch_blk); - lda(acc16_l); - cmp_i(0x01); - bne(uncrunch_blk); - jsr(write_output); - lda(alf_hdr_cksum_l); - cmp(cksum_l); - bne(print_emsg_checksum); - lda(alf_hdr_cksum_h); - cmp(cksum_h); - beq(cksum_ok); -print_emsg_checksum: - chksum_err(); /* does not exit */ -cksum_ok: - rts(); - -uncrunch_blk: - lda(acc16_l); - cmp_i(0x00); - bne(L760B); - lda(acc16_h); - cmp_i(0x01); - bne(L760B); - jsr(init_counters); - jsr(L76D0); - lda(acc16_l); - sta(L717D); - sta(L7179); - lda(acc16_h); - sta(L717E); - sta(L717A); - lda(acc16_l); - sta(L7177); - sta(L7178); - jsr(store_outbyte); - jmp(L75B1); - -L760B: - lda(acc16_l); - sta(L717D); - sta(L7175); - lda(acc16_h); - sta(L717E); - sta(L7176); - lda(acc16_h); - cmp(L717C); - bcc(L7641); - lda(acc16_l); - cmp(L717B); - bcc(L7641); - lda(L7179); - sta(acc16_l); - sta(L717D); - lda(L717A); - sta(acc16_h); - sta(L717E); - lda(L7178); - sta(acc16_l); - jsr(push_acc16); - -L7641: - lda(L717E); - beq(L7670); - lda(L717D); - sta(zp_b4); - lda(L717E); - sta(zp_b5); - jsr(L7899); - ldy_i(0x02); - lda_ind_y(zp_b0); - sta(acc16_l); - jsr(push_acc16); - ldy_i(0x00); - lda_ind_y(zp_b0); - sta(acc16_l); - sta(L717D); - iny(); - lda_ind_y(zp_b0); - sta(acc16_h); - sta(L717E); - jmp(L7641); - -L7670: - lda(L717D); - sta(acc16_l); - sta(L7178); - sta(L7177); - lda(L717E); - sta(acc16_h); - jsr(push_acc16); -L7683: - lda(L71AF); - ora(L71B0); - beq(L7694); - jsr(pop_acc16); - jsr(store_outbyte); - jmp(L7683); - -L7694: - jsr(L78C2); - lda(L7175); - sta(acc16_l); - sta(L7179); - lda(L7176); - sta(acc16_h); - sta(L717A); - lda(L717B); - sta(zp_b4); - lda(L717C); - sta(zp_b5); - cmp(L71AE); - bcc(L76CD); - lda(zp_b4); - cmp(L71AD); - bcc(L76CD); - lda(shift_counter); - cmp_i(0x0C); - beq(L76CD); - inc(shift_counter); - asl(L71AD); - rol(L71AE); -L76CD: - jmp(L75B1); -} - -void L76D0(void) { -L76D0: - lda(L71B6); - sta(zp_b8); - lda(L71B7); - sta(zp_b9); - ldx_i(0x02); -L76DC: - lsr(zp_b9); - ror(zp_b8); - dex(); - bpl(L76DC); - lda(zp_b8); - sta(L71AA); - lda(zp_b9); - sta(L71AB); - lda(inbuf_len_l); - sec(); - sbc(zp_b8); - sta(zp_bc); - lda(inbuf_len_h); - sbc(zp_b9); - sta(zp_bd); - lda(zp_bd); - bne(L770F); - lda(zp_bc); - cmp_i(0x03); - bcs(L770F); - ldx(L718C); - bpl(L771C); - cmp_i(0x02); - bcc(L7712); -L770F: - jmp(L779B); -/* ---------------------------------------------------------------------------- */ -L7712: - truncated_err(); /* exits! */ -/* ---------------------------------------------------------------------------- */ -L771C: - tay(); - dey(); - ldx(inbuf_adr_l); - stx(zp_be); - ldx(inbuf_adr_h); - stx(zp_bf); - lda(inbuf_adr_l); - clc(); - adc(zp_b8); - sta(zp_b8); - lda(inbuf_adr_h); - adc(zp_b9); - sta(zp_b9); -L7737: - lda_ind_y(zp_b8); - sta_ind_y(zp_be); - dey(); - bpl(L7737); - lda(inbuf_len_l); - sec(); - sbc(zp_bc); - sta(buf_len_l); - lda(inbuf_len_h); - sbc_i(0x00); - sta(buf_len_h); - lda(inbuf_adr_l); - clc(); - adc(zp_bc); - sta(buf_adr_l); - lda(inbuf_adr_h); - adc_i(0x00); - sta(buf_adr_h); - ldx_i(0x10); - jsr(L7A19); - sty(L718C); - bpl(L7771); - cpy_i(0x88); - beq(L7771); - // jmp(cleanup_and_exit); - exit(0); -/* ---------------------------------------------------------------------------- */ -L7771: - jsr(L79E7); - lda(L71AA); - sta(zp_b8); - lda(L71AB); - sta(zp_b9); - ldx_i(0x02); -L7780: - asl(zp_b8); - rol(zp_b9); - dex(); - bpl(L7780); - lda(L71B6); - sec(); - sbc(zp_b8); - sta(L71B6); - lda(L71B7); - sbc(zp_b9); - sta(L71B7); - jmp(L76D0); -/* ---------------------------------------------------------------------------- */ -L779B: - lda(zp_b8); - sta(zp_bc); - clc(); - adc(inbuf_adr_l); - sta(zp_b8); - lda(zp_b9); - sta(zp_bd); - adc(inbuf_adr_h); - sta(zp_b9); - ldy_i(0x00); - lda(L71B6); - and_i(0x07); - bne(L77E1); - lda_ind_y(zp_b8); - sta(acc16_h); - iny(); - lda_ind_y(zp_b8); - sta(acc16_l); -L77C0: - lda_i(0x0F); - sec(); - sbc(shift_counter); - tax(); -L77C7: - lsr(acc16_h); - ror(acc16_l); - dex(); - bpl(L77C7); - lda(shift_counter); - clc(); - adc(L71B6); - sta(L71B6); - lda_i(0x00); - adc(L71B7); - sta(L71B7); - rts(); - -/* ---------------------------------------------------------------------------- */ -L77E1: - ldx_i(0x02); -L77E3: - lda_ind_y(zp_b8); - sta_abs_x(L7189); - iny(); - dex(); - bpl(L77E3); - lda(L71B6); - and_i(0x07); - tax(); - dex(); -L77F3: - asl(L7189); - rol(L718A); - rol(L718B); - dex(); - bpl(L77F3); - lda(L718A); - sta(acc16_l); - lda(L718B); - sta(acc16_h); - jmp(L77C0); -} - -void L7A19(void) { - lda(alf_hdr_compsize2); - ora(alf_hdr_compsize3); - beq(L7A28); -L7A21: - jsr(readblock); - Y = 1; /* CIO would set this */ - jsr(L7A5D); - rts(); -L7A28: - lda(alf_hdr_compsize1); - cmp(buf_len_h); - bcc(L7A40); - beq(L7A34); - bcs(L7A21); -L7A34: - lda(alf_hdr_compsize0); - cmp(buf_len_l); - bcc(L7A40); - beq(L7A40); - bcs(L7A21); -L7A40: - lda(alf_hdr_compsize0); - sta(buf_len_l); - lda(alf_hdr_compsize1); - sta(buf_len_h); - lda(buf_len_l); - ora(buf_len_h); - beq(L7A57); - jsr(readblock); - Y = 1; /* CIO would set this */ -L7A57: - ldy_i(0x88); - jsr(L7A5D); - rts(); -} - -void L7A5D(void) { - lda(alf_hdr_compsize0); - sec(); - sbc(buf_len_l); - sta(alf_hdr_compsize0); - lda(alf_hdr_compsize1); - sbc(buf_len_h); - sta(alf_hdr_compsize1); - lda(alf_hdr_compsize2); - sbc_i(0x00); - sta(alf_hdr_compsize2); - rts(); -} - -void setup_io_bufs(void) { - lda(MEMTOP_lo); - sec(); - sbc_i(0xDC); - sta(inbuf_len_l); - lda(MEMTOP_hi); - sbc_i(0x7F); - sta(inbuf_len_h); - lsr(inbuf_len_h); - ror(inbuf_len_l); - lda(inbuf_len_h); - cmp_i(0x1F); - bcc(L79A0); - lda_i(0x00); - sta(inbuf_len_l); - lda_i(0x1F); - sta(inbuf_len_h); -L79A0: - lda_i(0xDC); - sta(inbuf_adr_l); - lda_i(0x7F); - sta(inbuf_adr_h); - lda_i(0xDC); - clc(); - adc(inbuf_len_l); - sta(outbuf_adr_l); - sta(outbuf_ptr_l); - lda_i(0x7F); - adc(inbuf_len_h); - sta(outbuf_adr_h); - sta(outbuf_ptr_h); - rts(); -} - -void init_counters(void) { - lda_i(0x09); - sta(shift_counter); - lda_i(0x00); - sta(L71AD); - lda_i(0x02); - sta(L71AE); - lda_i(0x02); - sta(L717B); - lda_i(0x01); - sta(L717C); - rts(); -} - -/* save decrunched byte in outbuf, update checksum, write outbuf if full */ -void store_outbyte(void) { - ldy_i(0x00); - lda(acc16_l); - sta_ind_y(outbuf_ptr_l); - clc(); - adc(cksum_l); - sta(cksum_l); - lda_i(0x00); - adc(cksum_h); - sta(cksum_h); - inc(outbuf_ptr_l); - bne(out_ptr_hi_ok); - inc(outbuf_ptr_h); -out_ptr_hi_ok: - inc(outbuf_len_l); - bne(out_len_hi_ok); - inc(outbuf_len_h); -out_len_hi_ok: - lda(outbuf_len_h); - cmp(inbuf_len_h); - bcc(outbuf_not_full); - lda(outbuf_len_l); - cmp(inbuf_len_l); - bcc(outbuf_not_full); - lda(outbuf_adr_l); - sta(buf_adr_l); - lda(outbuf_adr_h); - sta(buf_adr_h); - lda(outbuf_len_l); - sta(buf_len_l); - lda(outbuf_len_h); - sta(buf_len_h); - ldx_i(0x30); - jsr(writeblock); - bpl(init_outbuf); /* writeblock() never sets N flag, so always branch */ - chksum_err(); /* does not exit */ -/* ---------------------------------------------------------------------------- */ -init_outbuf: - lda(outbuf_adr_l); - sta(outbuf_ptr_l); - lda(outbuf_adr_h); - sta(outbuf_ptr_h); - lda_i(0x00); - sta(outbuf_len_l); - sta(outbuf_len_h); -outbuf_not_full: - rts(); -} - - -/* ---------------------------------------------------------------------------- */ -/* push 2 byte 'register' to software stack */ -void push_acc16(void) { - ldy_i(0x00); - lda(acc16_l); - sta_ind_y(stackptr_l); - iny(); - lda(acc16_h); - sta_ind_y(stackptr_l); - lda(stackptr_l); - clc(); - adc_i(0x02); - sta(stackptr_l); - bcc(L790C); - inc(stackptr_h); -L790C: - lda(stackptr_h); - cmp_i(0x70); - bcc(L791C); - stack_overrun(); /* exits! */ -/* ---------------------------------------------------------------------------- */ -L791C: - inc(L71AF); - bne(L7924); - inc(L71B0); -L7924: - rts(); -} - -void pop_acc16(void) { -/* pop 2 byte 'register' from software stack */ - lda(stackptr_l); - sec(); - sbc_i(0x02); - sta(stackptr_l); - lda(stackptr_h); - sbc_i(0x00); - sta(stackptr_h); - ldy_i(0x00); - lda_ind_y(stackptr_l); - sta(acc16_l); - iny(); - lda_ind_y(stackptr_l); - sta(acc16_h); - lda(stackptr_h); - cmp_i(0x60); - bcs(L796C); - stack_underrun(); /* exits! */ -/* ---------------------------------------------------------------------------- */ -L796C: - lda(L71AF); - bne(L7974); - dec(L71B0); -L7974: - dec(L71AF); - rts(); -} - -void L79E7(void) { - lda(buf_adr_l); - clc(); - adc(buf_len_l); - sta(zp_be); - lda(buf_adr_h); - adc(buf_len_h); - sta(zp_bf); - ldx_i(0x02); - bne(L7A0A); -L79FC: - ldy_i(0x00); - tya(); - sta_ind_y(zp_be); - inc(zp_be); - bne(L7A07); - inc(zp_bf); -L7A07: - dex(); - bmi(L7A18); -L7A0A: - lda(zp_bf); - cmp(outbuf_adr_h); - bcc(L79FC); - lda(zp_be); - cmp(outbuf_adr_l); - bcc(L79FC); -L7A18: - rts(); -} - -void L7899(void) { - lda(zp_b4); - sta(zp_b0); - lda(zp_b5); - sta(zp_b1); - asl(zp_b0); - rol(zp_b1); - lda(zp_b0); - clc(); - adc(zp_b4); - sta(zp_b0); - lda(zp_b1); - adc(zp_b5); - sta(zp_b1); - lda(zp_b0); - clc(); - adc(L7181); - sta(zp_b0); - lda(zp_b1); - adc(L7182); - sta(zp_b1); - rts(); -} - -void L78C2(void) { - lda(L717B); - sta(zp_b4); - lda(L717C); - sta(zp_b5); - jsr(L7899); - lda(L7177); - sta(acc16_l); - ldy_i(0x02); - sta_ind_y(zp_b0); - lda(L7179); - sta(acc16_l); - lda(L717A); - sta(acc16_h); - ldy_i(0x00); - lda(acc16_l); - sta_ind_y(zp_b0); - iny(); - lda(acc16_h); - sta_ind_y(zp_b0); - inc(L717B); - bne(L78F5); - inc(L717C); -L78F5: - rts(); -} - -void write_output(void) { - lda(outbuf_len_l); - ora(outbuf_len_h); - bne(have_output); - rts(); -/* ---------------------------------------------------------------------------- */ -have_output: - lda(outbuf_adr_l); - sta(buf_adr_l); - lda(outbuf_adr_h); - sta(buf_adr_h); - lda(outbuf_len_l); - sta(buf_len_l); - lda(outbuf_len_h); - sta(buf_len_h); - ldx_i(0x30); - jsr(writeblock); - rts(); -} diff --git a/src/extract.c b/src/extract.c deleted file mode 100644 index 305cd4a..0000000 --- a/src/extract.c +++ /dev/null @@ -1,197 +0,0 @@ -#include "unalf.h" -#include "addrs.h" - -int bad_checksum, bad_checksum_count = 0; -int new_file = 0; -unsigned int bytes_written = 0; -char *out_filename; - -void dpoke(int addr, u16 value) { - mem[addr] = value & 0xff; - mem[addr + 1] = value >> 8; -} - -u16 dpeek(int addr) { - return mem[addr] | (mem[addr + 1] << 8); -} - -static void bad_atari_filename(const char *msg) { - char fn[50] = ""; - char xbuf[5]; - int i; - u8 c; - - for(i = 0; (c = (u8)out_filename[i]) && i < 12; i++) { - if(c < ' ' || c > '|') { - /* not printable, insert a hex escape */ - sprintf(xbuf, "$%02X", c); - strcat(fn, xbuf); - } else { - strncat(fn, (char *)&c, 1); - } - } - - fprintf(stderr, "%s: bad Atari filename \"%s\": %s\n", self, fn, msg); -} - -/* note to self: it's tempting to use isalpha(), isprint(), etc - from ctype.h... but those are locale-aware. we want ASCII-only - versions. */ -static void sanity_check_filename(void) { - u8 c; - unsigned int i, bad = 0, dots = 0, uscore = 0; - - c = out_filename[0]; - if(!c) { - bad_atari_filename("empty! corrupt ALF file?"); - return; - } else if(c < 'A' || c > 'Z') { - bad_atari_filename("does not begin with A-Z"); - } - - for(i = 0; (c = out_filename[i]) && i < 12; i++) { - if(c >= 'A' && c <= 'Z') - continue; - if(c >= '0' && c <= '9') - continue; - if(c == '_') { - uscore++; - continue; - } - if(c == '.') { - dots++; - continue; - } - bad++; - } - - if(!dots) { - bad_atari_filename("no \".\""); - } else if(dots > 1) { - bad_atari_filename("more than one \".\""); - } - - if(bad) - bad_atari_filename("invalid characters. corrupt ALF file?"); - else if(uscore) - fprintf(stderr, "%s: filename has underscore, OK on Sparta/MyDOS, not Atari DOS 2.x\n", self); -} - -void fix_filename(void) { - char *p; - - if(!out_filename) return; - - if(strlen(out_filename) > 12) { - fprintf(stderr, "%s: filename in ALF header not null-terminated, fixing.\n", self); - out_filename[12] = '\0'; - } - - sanity_check_filename(); - - if(opts.lowercase) { - for(p = out_filename; *p; p++) - *p = tolower(*p); - } - - if(!opts.keepdot) { - for(p = out_filename; *p; p++) - if(p[0] == '.' && !p[1]) - *p = '\0'; - } -} - -void make_backup(void) { - /* up to 12-char FILENAME.EXT, plus a ~, plus null terminator = 14 */ - char backup[14]; - - strncpy(backup, out_filename, 13); - strncat(backup, "~", 13); - - /* silently ignore errors! */ - rename(out_filename, backup); -} - -void extract_alf(void) { - /* get ready to call fake 6502 stuff. set up memory like the Atari. */ - dpoke(MEMTOP, 0xbc1f); - - while(read_alf_header()) { - out_filename = (char *)(mem + alf_hdr_filename); - - fix_filename(); - - if(!file_wanted(out_filename)) { - if(fseek(in_file, getquad(alf_hdr_compsize0), SEEK_CUR) != 0) { - fprintf(stderr, "%s: fatal: seek failed on input!\n", self); - exit(1); - } - out_filename = 0; - continue; - } - - if(!opts.quiet) { - printf("%s %s\n", opts.testonly ? "Testing" : "Uncrunching", out_filename); - } - - if(opts.extract_to_stdout) { - out_file = stdout; - } else { - char *realname = out_filename; - - if(opts.testonly) { - out_filename = "/dev/null"; - } else if(!opts.overwrite) { - make_backup(); - } - - if(!(out_file = fopen(out_filename, "wb"))) { - fprintf(stderr, "%s: fatal: ", self); - perror(out_filename); - exit(1); - } - - out_filename = realname; - } - - bad_checksum = bytes_written = 0; - new_file = 1; - uncrunch_file(); - if(bad_checksum) bad_checksum_count++; - if(bytes_written != getquad(alf_hdr_origsize0)) - fprintf(stderr, "%s: %s should be %u bytes, but extracted to %u.\n", - self, out_filename, getquad(alf_hdr_origsize0), bytes_written); - - if(!opts.extract_to_stdout) fclose(out_file); - out_filename = 0; - } - - if(opts.testonly && !opts.quiet) { - if(bad_checksum_count) - printf("%d file%s with bad checksum!\n", - bad_checksum_count, - bad_checksum_count == 1 ? "" : "s"); - else - printf("All files OK.\n"); - } -} - -void chksum_err(void) { - bad_checksum = 1; - fprintf(stderr, "%s: checksum error on %s\n", self, out_filename); -} - -void truncated_err(void){ - fprintf(stderr, "%s: fatal: compressed data is truncated, EOF before end marker\n", self); - exit(1); -} - -void stack_underrun(void){ - fprintf(stderr, "%s: fatal: stack underrun\n", self); - exit(1); -} - -void stack_overrun(void){ - fprintf(stderr, "%s: fatal: stack overrun\n", self); - exit(1); -} diff --git a/src/glob.c b/src/glob.c deleted file mode 100644 index c816964..0000000 --- a/src/glob.c +++ /dev/null @@ -1,115 +0,0 @@ -#include "unalf.h" - -/* this is an Atari DOS or MS-DOS style match: "*.*" matches everything, - even files with no extension (no dot). "*" by itself matches only - files with no extension, as does "*." - matching is case-insensitive. */ - -static void split_filename(const char *fname, char *name, const char **ext) { - int i; - - for(i = 0; i < 8; i++) { - if(fname[i] == '.' || fname[i] == '\0') - break; - name[i] = fname[i]; - } - name[i] = '\0'; - - *ext = strchr(fname, '.'); - if(*ext) { - (*ext)++; - if(!**ext) *ext = 0; - } - - // fprintf(stderr, "split %s into: '%s' and '%s'\n", - // fname, name, (*ext ? *ext : "<none>")); -} - -static int match_part(const char *pat, const char *arg) { - char p; - - if(arg && arg[0] == '\0') arg = 0; - - if(!pat) { - if(arg) - return 0; - else - return 1; - } - - while((p = toupper(*pat++))) { - // fprintf(stderr, "p == %c\n", p); - switch(p) { - case '*': return 1; - case '?': - if(!arg || *arg == '\0') - return 0; - arg++; - break; - default: - if(!arg || toupper(*arg) != p) - return 0; - arg++; - break; - } - } - - return *arg ? 0 : 1; -} - -int globmatch(const char *pat, const char *arg) { - char npat[9], name[9]; - const char *epat = 0, *ext = 0; - - split_filename(pat, npat, &epat); - split_filename(arg, name, &ext); - - return match_part(npat, name) && match_part(epat, ext); -} - -int file_wanted(const char *filename) { - int i; - char **ig; - - /* if excluded, always return false */ - for(i = 0; i < exclude_count; i++) { - if(globmatch(exclude_globs[i], filename)) - return 0; - } - - /* if no include globs, always match (as though *.* were given) */ - if(!*include_globs) - return 1; - - ig = (char **)include_globs; - while(*ig) { - if(globmatch(*ig, filename)) - return 1; - ig++; - } - - return 0; -} - -/* for testing: */ -#if 0 -int main(int argc, char **argv) { - int matched; - const char *pattern; - - if(argc < 3) { - fprintf(stderr, "usage: %s <glob-pattern> <filename> ...\n", argv[0]); - return 1; - } - - pattern = argv[1]; - argv++; - - while(*++argv) { - matched = globmatch(pattern, *argv); - printf("%s %s %s\n", *argv, matched ? "matches" : "DOES NOT match", pattern); - } - - return 0; -} -#endif diff --git a/src/io.c b/src/io.c deleted file mode 100644 index 53bc0dd..0000000 --- a/src/io.c +++ /dev/null @@ -1,163 +0,0 @@ -#include "unalf.h" -#include "addrs.h" - -static int headers_read = 0; -static int convert_eols = 0; - -static void die_arc(void) { - fprintf(stderr, "%s: fatal: this is an ARC file, not ALF\n", self); - exit(1); -} - -static void die_not_alf(void) { - fprintf(stderr, "%s: fatal: not an ALF file\n", self); - exit(1); -} - -/* truncating the file by name could be a race condition, but I don't - think it's going to be a real-world problem for anyone. */ -static void eof_junk(long pos) { - fprintf(stderr, "%s: junk at EOF (%s)\n", self, opts.fixjunk ? "removing" : "ignoring"); - if(opts.fixjunk) { - if(truncate(in_filename, pos) < 0) { - fprintf(stderr, "%s: could not remove junk: ", self); - perror(in_filename); - } - } -} - -static void check_hdr_size(const char *name, unsigned long size) { - const char *desc; - - /* fits on a double-density disk? */ - if(size < 184320) - return; - - /* >= 16MB files are impossible because the decrunch algorithm - ignores the high byte of the 4-byte length. */ - if(size >= 16777216L) - desc = "impossibly large (>=16MB)"; - /* >1MB files are possible (e.g. with a hard drive on SpartaDOS X) - but exceedingly rare in the Atari world. */ - else if(size > 1048576L) - desc = "suspiciously large (>1MB)"; - else - desc = "too large for a floppy disk (>180KB)"; - - fprintf(stderr, "%s: header #%d %s size is %s.\n", - self, headers_read, name, desc); -} - -static void sanity_check_header(void) { - check_hdr_size("original", getquad(alf_hdr_origsize0)); - check_hdr_size("compressed", getquad(alf_hdr_compsize0)); -} - -/* return 1 if a header is read, 0 if not */ -int read_alf_header(void) { - u8 h1, h2; - int bytes; - long read_pos; - - read_pos = ftell(in_file); - - bytes = fread(mem + alf_header, 1, 29, in_file); - - if(!bytes) { - if(headers_read) - return 0; - else - die_not_alf(); - } else if(bytes < 29) { - if(headers_read) { - eof_junk(read_pos); - return 0; - } else { - die_not_alf(); - } - } - - h1 = mem[alf_header]; - h2 = mem[alf_hdr_sig]; - - if(h1 == 0x1a) { - if(h2 < 0x0f) die_arc(); - if(h2 == 0x0f) { - headers_read++; - sanity_check_header(); - return 1; /* signature matches */ - } - } - - if(headers_read) - eof_junk(read_pos); - else - die_not_alf(); - - return 0; -} - -/* read buf_len_l/h bytes into buf_adr_l/h, then store the number - of bytes actually read in buf_len_l/h. TODO: what about EOF? */ -void readblock(void) { - int bytes, len, bufadr; - u8 *buf; - - bufadr = dpeek(buf_adr_l); - buf = mem + bufadr; - len = dpeek(buf_len_l); - - // fprintf(stderr, "readblock, bufadr = $%04x, len = $%04x\n", bufadr, len); - - bytes = fread(buf, 1, len, in_file); - dpoke(buf_len_l, bytes); -} - -static int is_printable(u8 c) { - return (c == 0x9b || (c >= ' ' && c <= 124)); -} - -static int is_text_file(u8 *buf) { - return is_printable(buf[0]) && is_printable(buf[1]); -} - -/* mirror of readblock(), plus EOL conversion if needed. With -a, - a file is considered text if its first 2 bytes are printable ATASCII, - including EOLs. With -aa, all files are converted. */ -void writeblock(void) { - int i, bytes, len, bufadr; - u8 *buf; - - bufadr = dpeek(buf_adr_l); - buf = mem + bufadr; - len = dpeek(buf_len_l); - - if(new_file) { - if(opts.txtconv > 1) { - convert_eols = 1; - } else if(opts.txtconv == 1 && len > 1) { - convert_eols = is_text_file(buf); - } else { - convert_eols = 0; - } - } - new_file = 0; - - if(convert_eols) { - for(i = 0; i < len; i++) { - if(buf[i] == 0x9b) buf[i] = '\n'; - if(buf[i] == 0x7f) buf[i] = '\t'; - } - } - - // fprintf(stderr, "writeblock, bufadr = $%04x, len = $%04x\n", bufadr, len); - - bytes = fwrite(buf, 1, len, out_file); - if(bytes < 0) { - fprintf(stderr, "%s: fatal: ", self); - perror("write failed"); /* TODO: out_filename */ - exit(1); - } - bytes_written += bytes; - dpoke(buf_len_l, bytes); -} diff --git a/src/listalf.c b/src/listalf.c deleted file mode 100644 index 8082d11..0000000 --- a/src/listalf.c +++ /dev/null @@ -1,125 +0,0 @@ -#include "unalf.h" -#include "addrs.h" - -static const char *monthnames[] = { - "???", - "Jan", "Feb", "Mar", "Apr", - "May", "Jun", "Jul", "Aug", - "Sep", "Oct", "Nov", "Dec", - "???", "???", "???", -}; - -u16 getword(int offs) { - return mem[offs] | (mem[offs + 1] << 8); -} - -unsigned int getquad(int offs) { - return getword(offs) | ((getword(offs + 2) << 16)); -} - -/* see Arcinfo for details */ -void format_msdos_time(char *buf) { - u16 t, hour, min; - char ampm; - - t = getword(alf_hdr_time0); - - /* don't bother with seconds (arc doesn't print them either) */ - hour = t >> 11; - min = (t >> 5) & 0x3f; - - /* midnight is 12:00a (unlike arc, which uses 0:00a). noon - is 12:00p (same as arc). could just use a 24h clock like - unzip does, but AM/PM are more intuitive to me. */ - ampm = (hour > 11 ? 'p' : 'a'); - hour %= 12; - - /* midnight and noon print as 12, not 0 */ - if(hour == 0) hour = 12; - - snprintf(buf, 7, "%2d:%02d%c", hour, min, ampm); -} - -/* see Arcinfo for details */ -void format_msdos_date(char *buf) { - u16 d, year, month, day; - - d = getword(alf_hdr_date0); - - /* year actually ranges 1980 to 2107... */ - year = (d >> 9) + 1980; - year %= 100; /* ...but only print last 2 digits */ - - /* valid months range 1 to 12. values 0, 13, 14, 15 are possible - but invalid (print as ???) */ - month = (d >> 5) & 0x0f; - - /* valid day range is 1 to 31. 0 is invalid, print as-is. */ - day = d & 0x1f; - - snprintf(buf, 10, "%2d %3s %02d", day, monthnames[month], year); -} - -/* small files may be "compressed" larger than the original, so this - has to return a signed type. */ -static int comp_percent(unsigned int orig, unsigned int comp) { - if(orig == 0) return 0; /* no division by zero please */ - return 100 - (int)((float)comp / (float)orig * 100.0); -} - -/* output similar to "arc v", except we omit the compression type column - since there's only one type. also, don't call the checksum a CRC, it - isn't. */ -void list_alf(void) { - unsigned int c = 0, orig_size, comp_size, total_osize = 0, total_csize = 0; - char buf[100]; - extern char *out_filename; - - if(opts.verbose_list) { - puts("Name Length Size now Comp Date Time CkSum"); - puts("============ ======== ======== ==== ========= ====== ====="); - } - - while(read_alf_header()) { - c++; - orig_size = getquad(alf_hdr_origsize0); - comp_size = getquad(alf_hdr_compsize0); - out_filename = (char *)(mem + alf_hdr_filename); - - if(!opts.verbose_list) - fix_filename(); - - total_osize += orig_size; - total_csize += comp_size; - - if(opts.verbose_list) { - printf("%-12s ", out_filename); - printf("%8d ", orig_size); - printf("%8d ", comp_size); - printf("%3d%% ", comp_percent(orig_size, comp_size)); - format_msdos_date(buf); - printf("%9s ", buf); - format_msdos_time(buf); - printf("%6s ", buf); - printf(" %04x", getword(alf_hdr_cksum_l)); - putchar('\n'); - } else { - if(file_wanted(out_filename)) - printf("%s\n", out_filename); - } - - if(fseek(in_file, comp_size, SEEK_CUR) != 0) { - fprintf(stderr, "%s: fatal: seek failed on input!\n", self); - exit(1); - } - } - - if(opts.verbose_list) { - fputs(" ==== ======== ======== ====\nTotal ", stdout); - printf("%4d ", c); - printf("%8d ", total_osize); - printf("%8d ", total_csize); - printf("%3d%% ", comp_percent(total_osize, total_csize)); - putchar('\n'); - } -} diff --git a/src/manftr.rst b/src/manftr.rst deleted file mode 100644 index bb4c7ff..0000000 --- a/src/manftr.rst +++ /dev/null @@ -1,8 +0,0 @@ -The git repository for **unalf**: - - https://slackware.uk/~urchlay/repos/unalf - -Other Atari-related projects by the author, including **bw-atari8-tools** -and **unmac65**: - - https://slackware.uk/~urchlay/repos/ diff --git a/src/mkusage.pl b/src/mkusage.pl deleted file mode 100644 index 1c6a1bf..0000000 --- a/src/mkusage.pl +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/perl -w - -print "const char *usage_msg[] = {\n"; - -while(<>) { - chomp; - next if /^---/; - last if /^\.\. ENFOPTS/; - if(/^(?:\*\*)?(-[-a-zA-Z\d]+)/) { - $opt = $1; - next; - } - if($opt && (/^\.\. (.*)$/)) { - print "\t\" $opt: $1\",\n"; - undef $opt; - } -} - -print "\t(const char*)0\n};\n"; diff --git a/src/opts.c b/src/opts.c deleted file mode 100644 index 416163c..0000000 --- a/src/opts.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "unalf.h" - -#define OPTIONS "aefklLopqtvVd:x:" - -/* uncomment to test exclude/include glob lists */ -// #define DEBUG_GLOBS - -static void add_exclude(const char *glob) { - if(exclude_count == MAX_EXCLUDES) - return; - exclude_globs[exclude_count++] = glob; -} - -#ifdef DEBUG_GLOBS -static void show_globs(void) { - int i; - - printf("Include globs:\n"); - while(*include_globs) - printf(" + %s\n", *include_globs++); - printf("Exclude globs:\n"); - for(i = 0; i < exclude_count; i++) - printf(" - %s\n", exclude_globs[i]); - exit(0); -} -#endif - -void parse_opts(int argc, char * const *argv) { - int opt; - char **ig; - - /* don't let getopt() print error message for us. */ - opterr = 0; - - while((opt = getopt(argc, argv, OPTIONS)) != -1) { - switch(opt) { - case 'a': opts.txtconv++; break; - case 'e': opts.listonly = opts.testonly = 0; break; - case 'k': opts.keepdot++; break; - case 'f': opts.fixjunk++; opts.testonly = 1; opts.listonly = 0; opts.quiet = 1; break; - case 'l': opts.listonly++; opts.testonly = 0; break; - case 'L': opts.lowercase++; break; - case 'o': opts.overwrite++; break; - case 'p': opts.extract_to_stdout++; opts.quiet++; break; - case 'q': opts.quiet++; break; - case 't': opts.testonly++; opts.listonly = 0; break; - case 'v': opts.listonly = 1; opts.testonly = 0; opts.verbose_list++; break; - case 'V': puts(VERSION); exit(0); break; - case 'd': opts.outdir = optarg; break; - case 'x': add_exclude(optarg); break; - default: - fprintf(stderr, "%s: fatal: invalid option '-%c' (try -h or --help)\n", self, optopt); - exit(1); - } - } - - if(optind >= argc) { - fprintf(stderr, "%s: fatal: missing alf file argument (try -h or --help)\n", self); - exit(1); - } - - in_filename = argv[optind]; - if(optind < argc) - include_globs = &argv[optind + 1]; /* might be null, that's OK */ - - ig = (char **)include_globs; - while(*ig) { - if(globmatch("*.alf", *ig)) { - fprintf(stderr, "%s: ALF files don't normally contain other ALF files, are you trying to extract/list multiple ALF files at once?\n", self); - break; - } - ig++; - } - -#ifdef DEBUG_GLOBS - show_globs(); -#endif -} diff --git a/src/self.c b/src/self.c deleted file mode 100644 index 298608a..0000000 --- a/src/self.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "unalf.h" - -char *self; - -void set_self(char *argv0) { - char *p; - - self = argv0; - p = strrchr(self, '/'); - if(!p) p = strrchr(self, '\\'); // windows exe needs this - if(p) self = p + 1; -} - diff --git a/src/self.h b/src/self.h deleted file mode 100644 index aba1de2..0000000 --- a/src/self.h +++ /dev/null @@ -1,3 +0,0 @@ -/* self.c */ -extern char *self; -void set_self(char *argv0); diff --git a/src/unalf.1 b/src/unalf.1 deleted file mode 100644 index 3343162..0000000 --- a/src/unalf.1 +++ /dev/null @@ -1,412 +0,0 @@ -.\" Man page generated from reStructuredText. -. -. -.nr rst2man-indent-level 0 -. -.de1 rstReportMargin -\\$1 \\n[an-margin] -level \\n[rst2man-indent-level] -level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] -- -\\n[rst2man-indent0] -\\n[rst2man-indent1] -\\n[rst2man-indent2] -.. -.de1 INDENT -.\" .rstReportMargin pre: -. RS \\$1 -. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] -. nr rst2man-indent-level +1 -.\" .rstReportMargin post: -.. -.de UNINDENT -. RE -.\" indent \\n[an-margin] -.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] -.nr rst2man-indent-level -1 -.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] -.in \\n[rst2man-indent\\n[rst2man-indent-level]]u -.. -.TH "UNALF" 1 "2025-11-28" "0.2.0" "Urchlay's Atari 8-bit Tools" -.SH NAME -unalf \- extract Atari 8-bit ALF archives -.\" RST source for unalf(1) man page. Convert with: -. -.\" rst2man.py unalf.rst > unalf.1 -. -.SH SYNOPSIS -.sp -unalf [\fB\-\-help\fP] [\fB\-aehtklLopqtv\fP] [\fB\-d\fP \fIdir\fP] [\fB\-x\fP \fIwildcard\fP] \fIalf\-file\fP [\fIwildcard\fP ...] -.SH DESCRIPTION -.sp -\fBunalf\fP lists, extracts, or tests the contents of an \fIALF\fP archive. -.sp -\fIALF\fP is a compressed archive format similar to \fBarc\fP(1), though -not compatible with it. It was used on the Atari 8\-bit platform -beginning in the late 1980s. The Atari executables are \fBLZ.COM\fP -for the compressor and \fBDZ.COM\fP for the decompressor. \fBunalf\fP is a -port of \fBDZ.COM\fP for modern operating systems, with added features. -.sp -Extracted files are written to the current directory by -default. Existing files are backed up by default (by adding a \fB~\fP -suffix to the filename). -.sp -If one or more \fIwildcard\fP arguments are provided, files will only be -listed or extracted if they match one of the wildcards. See \fBWILDCARDS\fP -below for details. -.sp -Only one \fIalf\-file\fP argument is allowed. It\(aqs impossible to process -multiple ALF files per run of \fBunalf\fP\&. If you need bulk extraction, -use e.g. a \fBfor\fP loop in a shell script. -.sp -\fBunalf\fP can\(aqt read an ALF file on standard input; \fIalf\-file\fP must be -a real file. -.SH OPTIONS -.INDENT 0.0 -.TP -.B \-a -Convert text file line endings and tabs from ATASCII to ASCII. Text -files are detected by looking at the first 2 bytes of the extracted -file. If both are printable ASCII (including ATASCII EOL, aka \fB$9B\fP), the -file is considered text. -.sp -Note that \fIonly\fP line\-endings and tabs are converted. Other ATASCII -characters are left alone. If you need anything more in\-depth, use -\fBa8cat\fP(1). -.UNINDENT -.\" convert EOLs and tabs in text files. -. -.INDENT 0.0 -.TP -.B \fB\-aa\fP -Convert line endings and tabs in \fIall\fP extracted files. This will -corrupt any executables or non\-text data files, so use with caution. -.UNINDENT -.\" convert EOLs and tabs in ALL files. -. -.INDENT 0.0 -.TP -.BI \-d \ output\-dir -Write extracted files to this directory, which will be created if it -does not exist. The default is the current directory. -.UNINDENT -.\" set output directory (created if needed). -. -.INDENT 0.0 -.TP -.B \-e -Extract files. This is actually a no\-op, as extraction is the -default action. -.UNINDENT -.\" extract files (redundant; this is the default action). -. -.INDENT 0.0 -.TP -.B \-f -Fix (remove) junk at EOF. This option truncates the \fIalf\-file\fP\&. -It is the only \fBunalf\fP option that writes to the input file. No -backup is made. -.UNINDENT -.\" fix (remove) junk at EOF. -. -.INDENT 0.0 -.TP -.B \-h\fP,\fB \-\-help -Show built\-in help message. -.UNINDENT -.\" show this help message. -. -.INDENT 0.0 -.TP -.B \-k -Keep trailing periods. In most (all?) ALF files, filenames that have no -extension are stored with a trailing period, e.g. \(aq\fBFOO\fP\(aq would -be stored as \(aq\fBFOO.\fP\(aq. On the Atari, the extracted file would be -named \(aq\fBFOO\fP\(aq, so \fBunalf\fP does the same by default. With \fB\-k\fP, -the file will be named \(aq\fBFOO.\fP\(aq. -.UNINDENT -.\" keep trailing periods (dots) in filenames. -. -.INDENT 0.0 -.TP -.B \-l -List filenames in archive, one per line, but do not extract. Use -\fB\-v\fP for more information (file size, etc). Obeys \fIwildcards\fP and -the \fB\-x\fP, \fB\-L\fP, \fB\-k\fP options. -.UNINDENT -.\" list files in archive (filenames only). -. -.INDENT 0.0 -.TP -.B \-L -Lowercase filenames. Example: \fBFOO.TXT\fP will extract to \fBfoo.txt\fP\&. -.UNINDENT -.\" use lowercase filenames. -. -.INDENT 0.0 -.TP -.B \-o -Overwrite files, if they already exist. The default is to rename -existing files, adding a \fB~\fP suffix. Note that renaming \fBfile\fP -to \fBfile~\fP \fIwill\fP overwrite \fBfile~\fP if it already exists. -.UNINDENT -.\" overwrite files (do not create file~ backups). -. -.INDENT 0.0 -.TP -.B \-p -Extract to standard output. All the files that get extracted will be -printed to stdout with no delimiters, so this is most useful when -only extracting one file. \fB\-q\fP is automatically enabled by this option. -.sp -\fIWarning:\fP No checking is done to see whether stdout is a terminal. -Printing binary files to a terminal is a good way to make a mess. -Even text files with the EOLs translated can still contain ATASCII -formatting/graphics codes that can confuse your terminal. You have -been warned. -.UNINDENT -.\" extract to stdout (enables -q). -. -.INDENT 0.0 -.TP -.B \-q -Quiet extraction or testing: do not print "Uncrunching \fIfile\fP" -or "Testing \fIfile\fP" messages. Errors and warnings will still be -printed. This is the default when \fB\-p\fP is used. This option -doesn\(aqt do anything when used with \fB\-l\fP or \fB\-v\fP\&. -.UNINDENT -.\" quiet: don't print filenames during extraction/testing. -. -.INDENT 0.0 -.TP -.B \-t -Test archive. Same as extraction, except the files are not written -anywhere. -.UNINDENT -.\" test archive. -. -.INDENT 0.0 -.TP -.B \-v -Verbose listing of archive contents, with compressed and original -sizes, compression ration, date/time stamps, and checksum. The -output format is similar to that of \fBarc \-v\fP, minus the \fIStowage\fP -column, since \fIALF\fP doesn\(aqt support multiple compression types. The -date and time are displayed, but in most .alf files these are the -default values of "8 Jan 82 12:24a". -.sp -Unlike \fB\-l\fP, the \fB\-v\fP listing shows the filenames exactly as -stored in the archive (ignores \fB\-L\fP, enables \fB\-k\fP), and always -shows all the files (ignores \fIwildcards\fP and \fB\-x\fP). -.UNINDENT -.\" verbose listing of archive contents. -. -.INDENT 0.0 -.TP -.B \-V\fP,\fB \-\-version -Show \fBunalf\fP version number and exit. -.UNINDENT -.\" show version number. -. -.INDENT 0.0 -.TP -.BI \-x \ wildcard -Exclude (do not list or extract) files matching \fIwildcard\fP\&. See -\fBWILDCARDS\fP below. This option can be given multiple times -to exclude multiple patterns. -.UNINDENT -.\" exclude <wildcard>. may be given multiple times. -. -.\" ENDOPTS -. -.SH WILDCARDS -.sp -Wildcard (aka glob) matching works like it does on the Atari: \fI*.*\fP -matches any filename (whether or not it has an extension), and \fI*\fP -or \fI*.\fP only match filenames with no extension. -.sp -A \fI*\fP matches any number of characters (including zero). A \fI?\fP -matches a single character. All other characters match themselves, so -a wildcard with no \fI*\fP or \fI?\fP must match exactly. -.sp -Like the Atari, anything in the name or extension following a \fI*\fP is -ignored: \fBFOO*BAR.TXT\fP is equivalent to \fBFOO*.TXT\fP, and -\fBFOO.T*T\fP is equivalent to \fBFOO.T*\fP\&. \fBFOO*TXT\fP is -equivalent to \fBFOO*\fP, and \fIwill not\fP match the file \fBFOO.TXT\fP\&. -.sp -Unlike the Atari, matching is case\-insensitive. -.sp -To avoid your shell trying to expand the wildcards, always quote them! -Example: -.INDENT 0.0 -.INDENT 3.5 -.sp -.nf -.ft C -unalf filename.alf \(aq*.txt\(aq -.ft P -.fi -.UNINDENT -.UNINDENT -.SH EXIT STATUS -.INDENT 0.0 -.TP -.B 0 -Success. -.TP -.B 1 -Fatal error (I/O or bad command\-line arguments, stack under/overrun). -.TP -.B 2 -Processing completed OK, but at least one file in the archive -has a bad checksum. -.UNINDENT -.SH DIAGNOSTICS -.sp -Besides the standard error messages such as "no such file or directory": -.SS Fatal errors -.sp -\fBunalf\fP will exit with status 1 immediately after these errors: -.INDENT 0.0 -.TP -.B \fBfatal: this is an ARC file, not ALF\fP -Self\-explanatory. Use the \fBarc\fP(1) utility for this file. -.TP -.B \fBfatal: not an ALF file\fP -Self\-explanatory. Either the file is too small (less than 29 bytes) -or its first two bytes don\(aqt match the \fIALF\fP signature \fB0x1a\fP \fB0x0f\fP\&. -.TP -.B \fBfatal: stack underrun/overrun\fP -Generally means the archive is corrupt. Valid ALF files should never -cause these errors. -.TP -.B \fBfatal: compressed data is truncated, EOF before end marker\fP -Self\-explanatory. The ALF file ends before the compressed data does. -.UNINDENT -.SS Warnings -.sp -\fBunalf\fP will continue after these messages occur. -.INDENT 0.0 -.TP -.B \fBjunk at EOF (ignoring)\fP -Mostly harmless: does \fInot\fP imply data corruption. -.sp -Usually this is caused by the .alf file being stored on a CP/M disk -at some time, or by a dumb file transfer protocol. Either way, the -file gets padded to the block size of the filesystem or protocol. -Usually, the padding characters are \fB0x1a\fP, aka ASCII control\-Z. -.sp -This message means you can\(aqt append to the ALF file with \fBalf \-a\fP -or \fBLZ.COM\fP\&. You can, however, use \fBunalf \-f\fP to remove the junk. -.TP -.B \fBchecksum error on\fP \fI<file>\fP -The archive is corrupt. If \fI<file>\fP is a text file, it may be -partially readable. If it\(aqs an executable or other binary file, it\(aqs -probably unrecoverable. \fBunalf\fP will try to extract the rest of -the archive, but will exit with status 2 when it\(aqs done. -.sp -If you have an ALF file that does this, try it with the Atari -\fBDZ.COM\fP and see if it has the same problem. If it doesn\(aqt, -you\(aqve found a bug in \fBunalf\fP, please send me the .alf file via -email (see \fBAUTHOR\fP, below). Actually, I\(aqd be interested to see -the .alf file even if it doesn\(aqt work on the Atari either. -.TP -.B \fI<file>\fP \fBshould be\fP \fI<size>\fP \fBbytes, but extracted to\fP \fI<size>\fP -The archive is corrupt. If this happens on the last (or only) file, it -probably means the ALF file was truncated. You\(aqll almost certainly -get a checksum error for \fI<file>\fP, too. -.TP -.B \fBbad Atari filename\fP \fI<filename>\fP \fI<reason>\fP -The filename stored in the ALF header doesn\(aqt follow the rules for -Atari DOS filenames. \fI<reason>\fP will be something like "doesn\(aqt start -with A\-Z" or "invalid character". The filename will be printed with -any unprintable characters as hex values (e.g. \fB$01\fP). -.TP -.B \fBheader #<n> (compressed|original) size is...\fP -Followed by "impossibly large", "suspiciously large", or "too large -to fit on a floppy disk". May indicate a corrupt archive, or someone -really might have created an ALF file with files this big... though -"impossibly large" means >=16MB. \fBunalf\fP can\(aqt extract a file -that big. -.TP -.B \fBfilename has underscore, OK on Sparta/MyDOS, not Atari DOS 2.x\fP -Not an actual problem, unless you try to extract this ALF file on -an Atari running Atari DOS 2.0/2.5, or any other DOS that doesn\(aqt -allow underscores in filenames. -.TP -.B \fBALF files don\(aqt normally contain other ALF files, are you trying to extract/list multiple ALF files at once?\fP -You gave a \fIwildcard\fP argument that ends with \fI\&.alf\fP\&. This is OK if -your \fIALF\fP file actually contains another \fIALF\fP file, but that would -be a pathological case. Most likely, you\(aqre trying to extract multiple -\fIALF\fP files with one command, which doesn\(aqt work. Use one \fBunalf\fP -command per file. -.UNINDENT -.SH NOTES -.sp -This \fBunalf\fP is 100% compatible with the original Atari \fBDZ.COM\fP -aka \fBUNALF.COM\fP, with the following differences: -.INDENT 0.0 -.IP \(bu 2 -There is no interactive mode. The file to extract must be given as -a command\-line argument, and the output directory must be given -with the \fB\-d\fP option. -.IP \(bu 2 -This \fBunalf\fP is capable of listing or testing the contents of an archive -without extracting it, plus other handy features such as extracting -to standard output and text file EOL conversion. -.IP \(bu 2 -Turning the screen off for speed makes no sense on modern operating -systems, so there\(aqs no option for that. -.UNINDENT -.sp -Neither this \fBunalf\fP nor \fBDZ.COM\fP actually use the dates/times -stored in the archive. Extracted files will have their timestamps set -to the current date/time. -.sp -A note about the Atari filenames: \fBDZ.COM\fP is sometimes found on -old disk images as \fBUNALF.COM\fP, and \fBLZ.COM\fP is sometimes called -\fBALF.COM\fP or \fBALFER.COM\fP\&. I\(aqve used the original names partly -out of respect for the original author, and partly to avoid confusion -between my \fBunalf\fP and the Atari one. -.SH BUGS -.sp -A minor one: \fBunalf\fP can\(aqt correctly extract files larger than about -15MB. This could be a problem in \fBalf\fP (though the archives look -OK to me), or it could be a bug I introduced when porting \fBunalf\fP, -or it could be a bug in the original \fBDZ.COM\fP\&. Files this large are -a pathological case for \fIALF\fP, anyway, since nobody ever used them on -the Atari 8\-bit. -.SH COPYRIGHT -.sp -The original AlfCrunch (\fBDZ.COM\fP and \fBLZ.COM\fP) for the Atari -8\-bit was released into the Public Domain. Portions of the \fBDZ.COM\fP -code are included in \fBunalf\fP, converted from 6502 instructions to -C macros. -.sp -\fBunalf\fP is released under the WTPFL: Do WTF you want with this. -.SH AUTHOR -.sp -The original AlfCrunch for the Atari 8\-bit was written by Alfred, who -can be reached via the AtariAge.com forums with the username "Alfred". -.sp -This \fBunalf\fP was written by B. Watson <\fI\%urchlay@slackware.uk\fP>. -.SH SEE ALSO -.sp -\fBalf\fP(1), \fBalfls\fP(1) -.sp -The git repository for \fBunalf\fP: -.INDENT 0.0 -.INDENT 3.5 -\fI\%https://slackware.uk/~urchlay/repos/unalf\fP -.UNINDENT -.UNINDENT -.sp -Other Atari\-related projects by the author, including \fBbw\-atari8\-tools\fP -and \fBunmac65\fP: -.INDENT 0.0 -.INDENT 3.5 -\fI\%https://slackware.uk/~urchlay/repos/\fP -.UNINDENT -.UNINDENT -.\" Generated by docutils manpage writer. -. diff --git a/src/unalf.c b/src/unalf.c deleted file mode 100644 index 545a5c2..0000000 --- a/src/unalf.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "unalf.h" - -FILE *in_file, *out_file; -char *in_filename; -opts_t opts; -const char *exclude_globs[MAX_EXCLUDES]; -int exclude_count; -char * const *include_globs; -static void create_outdir(void); - -/* like "mkdir -p" (no error if dir already exists), - followed by "cd" (which will error if the existing - "directory" turns out to be a file or broken symlink */ -static void create_outdir(void) { - int r; - - r = -#if defined(__MINGW32__) || defined(__MINGW64__) - mkdir(opts.outdir); -#else - mkdir(opts.outdir, 0777); -#endif - if(r < 0 && errno != EEXIST) { - fprintf(stderr, "%s: fatal: ", self); - perror(opts.outdir); - exit(1); - } - - if(chdir(opts.outdir) < 0) { - fprintf(stderr, "%s: fatal: ", self); - perror(opts.outdir); - exit(1); - } -} - -void usage(void) { - extern char *usage_msg[]; - char **line; - - puts("unalf (ALF extractor) v" VERSION " by B. Watson. WTFPL."); - printf("Usage: %s -[options] <file> [wildcard ...]\n", self); - puts("Options:"); - puts(" wildcards: extract only matching files."); - - for(line = usage_msg; *line; line++) - puts(*line); - - exit(0); -} - -int main(int argc, char **argv) { - set_self(argv[0]); - - if(argc < 2 || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) usage(); - - if(!strcmp(argv[1], "--version")) { - puts(VERSION); - exit(0); - } - - parse_opts(argc, argv); - - if(!(in_file = fopen(in_filename, "rb"))) { - fprintf(stderr, "%s: fatal: ", self); - perror(in_filename); - exit(1); - } - - if(opts.outdir) create_outdir(); - - if(opts.listonly) - list_alf(); - else - extract_alf(); - - fclose(in_file); - - exit(bad_checksum_count ? 2 : 0); -} diff --git a/src/unalf.h b/src/unalf.h deleted file mode 100644 index 743282d..0000000 --- a/src/unalf.h +++ /dev/null @@ -1,93 +0,0 @@ -#include <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <ctype.h> -#include <sys/stat.h> -#include <f65.h> -#include "self.h" - -#ifndef VERSION -#define VERSION "???" -#warning "VERSION not defined, defaulting to \"???\"" -#endif - -#ifndef u8 -#define u8 unsigned char -#define u16 unsigned short -#endif - -/* asmcode.c (converted from asm) */ -void uncrunch_file(void); -void setup_io_bufs(void); -void L7A19(void); -void L79E7(void); -void L76D0(void); -void write_output(void); -void init_counters(void); -void store_outbyte(void); -void push_acc16(void); -void L7899(void); -void pop_acc16(void); -void L78C2(void); -void L7A19(void); -void L79E7(void); -void writeblock(void); -void L7A5D(void); - -/* glob.c */ -int globmatch(const char *pat, const char *arg); -int file_wanted(const char *filename); - -/* io.c - asm rewritten in C */ -int read_alf_header(void); -void readblock(void); -void writeblock(void); -void write_output(void); - -/* io.c - just C */ -void open_output(void); - -/* listalf.c */ -void list_alf(void); -unsigned int getquad(int offs); - -/* extract.c */ -extern int bad_checksum_count; -extern int new_file; -extern unsigned int bytes_written; -void extract_alf(void); -void dpoke(int addr, u16 value); -u16 dpeek(int addr); -void fix_filename(void); - -/* opts.c */ -void parse_opts(int argc, char * const *argv); - -/* unalf.c */ -extern char *self; -extern FILE *out_file; -extern FILE *in_file; -extern char *in_filename; - -typedef struct { - int txtconv; - int testonly; - int keepdot; - int listonly; - int lowercase; - const char *outdir; - int overwrite; - int extract_to_stdout; - int quiet; - int verbose_list; - int fixjunk; -} opts_t; - -#define MAX_EXCLUDES 256 - -extern opts_t opts; -extern const char *exclude_globs[MAX_EXCLUDES]; -extern char * const *include_globs; -extern int exclude_count; diff --git a/src/unalf.rst b/src/unalf.rst deleted file mode 100644 index e94fc21..0000000 --- a/src/unalf.rst +++ /dev/null @@ -1,358 +0,0 @@ -.. RST source for unalf(1) man page. Convert with: -.. rst2man.py unalf.rst > unalf.1 - -.. include:: ver.rst - -.. |date| date:: - -===== -unalf -===== - --------------------------------- -extract Atari 8-bit ALF archives --------------------------------- - -:Manual section: 1 -:Manual group: Urchlay's Atari 8-bit Tools -:Date: |date| -:Version: |version| - -SYNOPSIS -======== - -unalf [**--help**] [**-aehtklLopqtv**] [**-d** *dir*] [**-x** *wildcard*] *alf-file* [*wildcard* ...] - -DESCRIPTION -=========== - -**unalf** lists, extracts, or tests the contents of an *ALF* archive. - -*ALF* is a compressed archive format similar to **arc**\(1), though -not compatible with it. It was used on the Atari 8-bit platform -beginning in the late 1980s. The Atari executables are **LZ.COM** -for the compressor and **DZ.COM** for the decompressor. **unalf** is a -port of **DZ.COM** for modern operating systems, with added features. - -Extracted files are written to the current directory by -default. Existing files are backed up by default (by adding a **~** -suffix to the filename). - -If one or more *wildcard* arguments are provided, files will only be -listed or extracted if they match one of the wildcards. See **WILDCARDS** -below for details. - -Only one *alf-file* argument is allowed. It's impossible to process -multiple ALF files per run of **unalf**. If you need bulk extraction, -use e.g. a **for** loop in a shell script. - -**unalf** can't read an ALF file on standard input; *alf-file* must be -a real file. - -OPTIONS -======= - --a - Convert text file line endings and tabs from ATASCII to ASCII. Text - files are detected by looking at the first 2 bytes of the extracted - file. If both are printable ASCII (including ATASCII EOL, aka **$9B**), the - file is considered text. - - Note that *only* line-endings and tabs are converted. Other ATASCII - characters are left alone. If you need anything more in-depth, use - **a8cat**\(1). - -.. convert EOLs and tabs in text files. - -**-aa** - Convert line endings and tabs in *all* extracted files. This will - corrupt any executables or non-text data files, so use with caution. - -.. convert EOLs and tabs in ALL files. - --d output-dir - Write extracted files to this directory, which will be created if it - does not exist. The default is the current directory. - -.. set output directory (created if needed). - --e - Extract files. This is actually a no-op, as extraction is the - default action. - -.. extract files (redundant; this is the default action). - --f - Fix (remove) junk at EOF. This option truncates the *alf-file*. - It is the only **unalf** option that writes to the input file. No - backup is made. - -.. fix (remove) junk at EOF. - --h, --help - Show built-in help message. - -.. show this help message. - --k - Keep trailing periods. In most (all?) ALF files, filenames that have no - extension are stored with a trailing period, e.g. '**FOO**' would - be stored as '**FOO.**'. On the Atari, the extracted file would be - named '**FOO**', so **unalf** does the same by default. With **-k**, - the file will be named '**FOO.**'. - -.. keep trailing periods (dots) in filenames. - --l - List filenames in archive, one per line, but do not extract. Use - **-v** for more information (file size, etc). Obeys *wildcards* and - the **-x**, **-L**, **-k** options. - -.. list files in archive (filenames only). - --L - Lowercase filenames. Example: **FOO.TXT** will extract to **foo.txt**. - -.. use lowercase filenames. - --o - Overwrite files, if they already exist. The default is to rename - existing files, adding a **~** suffix. Note that renaming **file** - to **file~** *will* overwrite **file~** if it already exists. - -.. overwrite files (do not create file~ backups). - --p - Extract to standard output. All the files that get extracted will be - printed to stdout with no delimiters, so this is most useful when - only extracting one file. **-q** is automatically enabled by this option. - - *Warning:* No checking is done to see whether stdout is a terminal. - Printing binary files to a terminal is a good way to make a mess. - Even text files with the EOLs translated can still contain ATASCII - formatting/graphics codes that can confuse your terminal. You have - been warned. - -.. extract to stdout (enables -q). - --q - Quiet extraction or testing: do not print "Uncrunching *file*" - or "Testing *file*" messages. Errors and warnings will still be - printed. This is the default when **-p** is used. This option - doesn't do anything when used with **-l** or **-v**. - -.. quiet: don't print filenames during extraction/testing. - --t - Test archive. Same as extraction, except the files are not written - anywhere. - -.. test archive. - --v - Verbose listing of archive contents, with compressed and original - sizes, compression ration, date/time stamps, and checksum. The - output format is similar to that of **arc -v**, minus the *Stowage* - column, since *ALF* doesn't support multiple compression types. The - date and time are displayed, but in most .alf files these are the - default values of "8 Jan 82 12:24a". - - Unlike **-l**, the **-v** listing shows the filenames exactly as - stored in the archive (ignores **-L**, enables **-k**), and always - shows all the files (ignores *wildcards* and **-x**). - -.. verbose listing of archive contents. - --V, --version - Show **unalf** version number and exit. - -.. show version number. - --x wildcard - Exclude (do not list or extract) files matching *wildcard*. See - **WILDCARDS** below. This option can be given multiple times - to exclude multiple patterns. - -.. exclude <wildcard>. may be given multiple times. - -.. ENDOPTS - -WILDCARDS -========= - -Wildcard (aka glob) matching works like it does on the Atari: *\*.\** -matches any filename (whether or not it has an extension), and *\** -or *\*.* only match filenames with no extension. - -A *\** matches any number of characters (including zero). A *?* -matches a single character. All other characters match themselves, so -a wildcard with no *\** or *?* must match exactly. - -Like the Atari, anything in the name or extension following a *\** is -ignored: **FOO\*BAR.TXT** is equivalent to **FOO\*.TXT**, and -**FOO.T\*T** is equivalent to **FOO.T\***. **FOO\*TXT** is -equivalent to **FOO\***, and *will not* match the file **FOO.TXT**. - -Unlike the Atari, matching is case-insensitive. - -To avoid your shell trying to expand the wildcards, always quote them! -Example:: - - unalf filename.alf '*.txt' - -EXIT STATUS -=========== - -0 - Success. - -1 - Fatal error (I/O or bad command-line arguments, stack under/overrun). - -2 - Processing completed OK, but at least one file in the archive - has a bad checksum. - -DIAGNOSTICS -=========== - -Besides the standard error messages such as "no such file or directory": - -Fatal errors ------------- - -**unalf** will exit with status 1 immediately after these errors: - -**fatal: this is an ARC file, not ALF** - Self-explanatory. Use the **arc**\(1) utility for this file. - -**fatal: not an ALF file** - Self-explanatory. Either the file is too small (less than 29 bytes) - or its first two bytes don't match the *ALF* signature **0x1a** **0x0f**\. - -**fatal: stack underrun/overrun** - Generally means the archive is corrupt. Valid ALF files should never - cause these errors. - -**fatal: compressed data is truncated, EOF before end marker** - Self-explanatory. The ALF file ends before the compressed data does. - -Warnings --------- - -**unalf** will continue after these messages occur. - -**junk at EOF (ignoring)** - Mostly harmless: does *not* imply data corruption. - - Usually this is caused by the .alf file being stored on a CP/M disk - at some time, or by a dumb file transfer protocol. Either way, the - file gets padded to the block size of the filesystem or protocol. - Usually, the padding characters are **0x1a**, aka ASCII control-Z. - - This message means you can't append to the ALF file with **alf -a** - or **LZ.COM**. You can, however, use **unalf -f** to remove the junk. - -**checksum error on** *<file>* - The archive is corrupt. If *<file>* is a text file, it may be - partially readable. If it's an executable or other binary file, it's - probably unrecoverable. **unalf** will try to extract the rest of - the archive, but will exit with status 2 when it's done. - - If you have an ALF file that does this, try it with the Atari - **DZ.COM** and see if it has the same problem. If it doesn't, - you've found a bug in **unalf**, please send me the .alf file via - email (see **AUTHOR**, below). Actually, I'd be interested to see - the .alf file even if it doesn't work on the Atari either. - -*<file>* **should be** *<size>* **bytes, but extracted to** *<size>* - The archive is corrupt. If this happens on the last (or only) file, it - probably means the ALF file was truncated. You'll almost certainly - get a checksum error for *<file>*, too. - -**bad Atari filename** *<filename>* *<reason>* - The filename stored in the ALF header doesn't follow the rules for - Atari DOS filenames. *<reason>* will be something like "doesn't start - with A-Z" or "invalid character". The filename will be printed with - any unprintable characters as hex values (e.g. **$01**). - -**header #<n> (compressed|original) size is...** - Followed by "impossibly large", "suspiciously large", or "too large - to fit on a floppy disk". May indicate a corrupt archive, or someone - really might have created an ALF file with files this big... though - "impossibly large" means >=16MB. **unalf** can't extract a file - that big. - -**filename has underscore, OK on Sparta/MyDOS, not Atari DOS 2.x** - Not an actual problem, unless you try to extract this ALF file on - an Atari running Atari DOS 2.0/2.5, or any other DOS that doesn't - allow underscores in filenames. - -**ALF files don't normally contain other ALF files, are you trying to extract/list multiple ALF files at once?** - You gave a *wildcard* argument that ends with *.alf*. This is OK if - your *ALF* file actually contains another *ALF* file, but that would - be a pathological case. Most likely, you're trying to extract multiple - *ALF* files with one command, which doesn't work. Use one **unalf** - command per file. - -NOTES -===== - -This **unalf** is 100% compatible with the original Atari **DZ.COM** -aka **UNALF.COM**, with the following differences: - -- There is no interactive mode. The file to extract must be given as - a command-line argument, and the output directory must be given - with the **-d** option. - -- This **unalf** is capable of listing or testing the contents of an archive - without extracting it, plus other handy features such as extracting - to standard output and text file EOL conversion. - -- Turning the screen off for speed makes no sense on modern operating - systems, so there's no option for that. - -Neither this **unalf** nor **DZ.COM** actually use the dates/times -stored in the archive. Extracted files will have their timestamps set -to the current date/time. - -A note about the Atari filenames: **DZ.COM** is sometimes found on -old disk images as **UNALF.COM**, and **LZ.COM** is sometimes called -**ALF.COM** or **ALFER.COM**. I've used the original names partly -out of respect for the original author, and partly to avoid confusion -between my **unalf** and the Atari one. - -BUGS -==== - -A minor one: **unalf** can't correctly extract files larger than about -15MB. This could be a problem in **alf** (though the archives look -OK to me), or it could be a bug I introduced when porting **unalf**, -or it could be a bug in the original **DZ.COM**. Files this large are -a pathological case for *ALF*, anyway, since nobody ever used them on -the Atari 8-bit. - -COPYRIGHT -========= - -The original AlfCrunch (**DZ.COM** and **LZ.COM**) for the Atari -8-bit was released into the Public Domain. Portions of the **DZ.COM** -code are included in **unalf**, converted from 6502 instructions to -C macros. - -**unalf** is released under the WTPFL: Do WTF you want with this. - -AUTHOR -====== - -The original AlfCrunch for the Atari 8-bit was written by Alfred, who -can be reached via the AtariAge.com forums with the username "Alfred". - -This **unalf** was written by B. Watson <urchlay@slackware.uk>. - -SEE ALSO -======== - -**alf**\(1), **alfls**\(1) - -.. include:: manftr.rst diff --git a/src/usage.c b/src/usage.c deleted file mode 100644 index 7615d2e..0000000 --- a/src/usage.c +++ /dev/null @@ -1,19 +0,0 @@ -const char *usage_msg[] = { - " -a: convert EOLs and tabs in text files.", - " -aa: convert EOLs and tabs in ALL files.", - " -d: set output directory (created if needed).", - " -e: extract files (redundant; this is the default action).", - " -f: fix (remove) junk at EOF.", - " -h: show this help message.", - " -k: keep trailing periods (dots) in filenames.", - " -l: list files in archive (filenames only).", - " -L: use lowercase filenames.", - " -o: overwrite files (do not create file~ backups).", - " -p: extract to stdout (enables -q).", - " -q: quiet: don't print filenames during extraction/testing.", - " -t: test archive.", - " -v: verbose listing of archive contents.", - " -V: show version number.", - " -x: exclude <wildcard>. may be given multiple times.", - (const char*)0 -}; diff --git a/src/ver.rst b/src/ver.rst deleted file mode 100644 index 2c33076..0000000 --- a/src/ver.rst +++ /dev/null @@ -1 +0,0 @@ -.. |version| replace:: 0.2.0 |
