Allocate clusters and build directory entries
This commit is contained in:
parent
9a43bb46bf
commit
600bfb3764
189
tar2fat.scm
189
tar2fat.scm
|
@ -1,3 +1,9 @@
|
||||||
|
(define (filter p list)
|
||||||
|
(let loop ((newlist '()) (list list))
|
||||||
|
(if (null? list) (reverse newlist)
|
||||||
|
(loop (if (p (car list)) (cons (car list) newlist) newlist)
|
||||||
|
(cdr list)))))
|
||||||
|
|
||||||
(define (find p list)
|
(define (find p list)
|
||||||
(cond ((null? list) #f)
|
(cond ((null? list) #f)
|
||||||
((p (car list)) (car list))
|
((p (car list)) (car list))
|
||||||
|
@ -9,6 +15,22 @@
|
||||||
(if (null? (cdr list)) (reverse newlist)
|
(if (null? (cdr list)) (reverse newlist)
|
||||||
(loop (cons (car list) newlist) (cdr list))))))
|
(loop (cons (car list) newlist) (cdr list))))))
|
||||||
|
|
||||||
|
(define (bytevector-concatenate bytevectors)
|
||||||
|
(fold (lambda (part whole) (bytevector-append whole part))
|
||||||
|
(bytevector) bytevectors))
|
||||||
|
|
||||||
|
(define (string-index str char)
|
||||||
|
(let loop ((i 0))
|
||||||
|
(cond ((= i (string-length str)) #f)
|
||||||
|
((char=? char (string-ref str i)) i)
|
||||||
|
(else (loop (+ i 1))))))
|
||||||
|
|
||||||
|
(define (string-every? p str)
|
||||||
|
(let loop ((i 0))
|
||||||
|
(or (= i (string-length str))
|
||||||
|
(and (p (string-ref str i))
|
||||||
|
(loop (+ i 1))))))
|
||||||
|
|
||||||
(define (string-prefix? fix str)
|
(define (string-prefix? fix str)
|
||||||
(let ((fixlen (string-length fix))
|
(let ((fixlen (string-length fix))
|
||||||
(strlen (string-length str)))
|
(strlen (string-length str)))
|
||||||
|
@ -19,36 +41,68 @@
|
||||||
(define (string-drop str n)
|
(define (string-drop str n)
|
||||||
(string-copy str n (string-length str)))
|
(string-copy str n (string-length str)))
|
||||||
|
|
||||||
|
(define (string-pad str n char)
|
||||||
|
(let ((lack (- n (string-length str))))
|
||||||
|
(if (<= lack 0) str (string-append str (make-string lack char)))))
|
||||||
|
|
||||||
|
(define (u8 u)
|
||||||
|
(bytevector u))
|
||||||
|
|
||||||
|
(define (u16l u)
|
||||||
|
(bytevector (bitwise-and #xff u)
|
||||||
|
(bitwise-and #xff (arithmetic-shift u -8))))
|
||||||
|
|
||||||
|
(define (u32l u)
|
||||||
|
(bytevector (bitwise-and #xff u)
|
||||||
|
(bitwise-and #xff (arithmetic-shift u -8))
|
||||||
|
(bitwise-and #xff (arithmetic-shift u -16))
|
||||||
|
(bitwise-and #xff (arithmetic-shift u -24))))
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
(define bytes/sector 512) ; Do not change.
|
||||||
|
(define sectors/cluster 8)
|
||||||
|
|
||||||
(define-record-type fat-directory
|
(define-record-type fat-directory
|
||||||
(make-fat-directory* name entries)
|
(make-fat-directory* name entries)
|
||||||
fat-directory?
|
fat-directory?
|
||||||
(name fat-directory-name)
|
(name fat-directory-name)
|
||||||
(entries fat-directory-entries fat-directory-set-entries!))
|
(entries fat-directory-entries fat-directory-set-entries!)
|
||||||
|
(first-cluster fat-directory-first-cluster
|
||||||
|
fat-directory-set-first-cluster!))
|
||||||
|
|
||||||
(define-record-type fat-file
|
(define-record-type fat-file
|
||||||
(make-fat-file name contents)
|
(make-fat-file name contents)
|
||||||
fat-file?
|
fat-file?
|
||||||
(name fat-file-name)
|
(name fat-file-name)
|
||||||
(contents fat-file-contents))
|
(contents fat-file-contents)
|
||||||
|
(first-cluster fat-file-first-cluster fat-file-set-first-cluster!))
|
||||||
|
|
||||||
(define-record-type drive
|
(define-record-type drive
|
||||||
(make-drive* number root-directory)
|
(make-drive* number root-directory)
|
||||||
drive?
|
drive?
|
||||||
(number drive-number)
|
(number drive-number)
|
||||||
|
(first-sector drive-first-sector)
|
||||||
|
(sector-count drive-sector-count drive-set-sector-count!)
|
||||||
|
(max-space drive-max-space)
|
||||||
|
(free-space drive-free-space)
|
||||||
(root-directory drive-root-directory))
|
(root-directory drive-root-directory))
|
||||||
|
|
||||||
(define first-drive-letter #\C)
|
(define first-drive-letter #\c)
|
||||||
(define last-drive-letter #\F)
|
(define last-drive-letter #\f)
|
||||||
|
|
||||||
(define max-drives
|
(define max-drives
|
||||||
(- (char->integer last-drive-letter) (char->integer first-drive-letter)))
|
(+ 1 (- (char->integer last-drive-letter)
|
||||||
|
(char->integer first-drive-letter))))
|
||||||
|
|
||||||
(define drives (make-vector max-drives #f))
|
(define drives (make-vector max-drives #f))
|
||||||
|
|
||||||
|
(define (get-used-drives)
|
||||||
|
(filter drive? (vector->list drives)))
|
||||||
|
|
||||||
(define (drive-letter drive)
|
(define (drive-letter drive)
|
||||||
(integer->char (+ (char->integer first-drive-letter) (drive-number drive))))
|
(string (integer->char (+ (char->integer first-drive-letter)
|
||||||
|
(drive-number drive)))))
|
||||||
|
|
||||||
(define (drive-by-letter letter)
|
(define (drive-by-letter letter)
|
||||||
(let ((number (- (char->integer letter) first-drive-letter)))
|
(let ((number (- (char->integer letter) first-drive-letter)))
|
||||||
|
@ -122,6 +176,12 @@
|
||||||
(else
|
(else
|
||||||
(error "Can't decide if entry is file or directory")))))))
|
(error "Can't decide if entry is file or directory")))))))
|
||||||
|
|
||||||
|
(define (fat-file-size file)
|
||||||
|
(bytevector-length (fat-file-contents file)))
|
||||||
|
|
||||||
|
(define (fat-file-clusters file)
|
||||||
|
(ceiling (/ (fat-file-size file) (* bytes/sector sectors/cluster))))
|
||||||
|
|
||||||
(define (tar-read-entry)
|
(define (tar-read-entry)
|
||||||
|
|
||||||
(define (bytevector-every? match? bytes)
|
(define (bytevector-every? match? bytes)
|
||||||
|
@ -199,23 +259,7 @@
|
||||||
(loop a (+ b 1) parts))))))
|
(loop a (+ b 1) parts))))))
|
||||||
parts))
|
parts))
|
||||||
|
|
||||||
(define (dump-drive letter)
|
(define (input-entry entry)
|
||||||
(define (emit nest str)
|
|
||||||
(write-string (make-string (* 2 nest) #\space))
|
|
||||||
(write-string str)
|
|
||||||
(newline))
|
|
||||||
(define (dump-directory-entries nest directory)
|
|
||||||
(for-each (lambda (entry)
|
|
||||||
(emit nest (fat-directory-entry-name entry))
|
|
||||||
(when (fat-directory? entry)
|
|
||||||
(dump-directory-entries (+ nest 1) entry)))
|
|
||||||
(fat-directory-entries directory)))
|
|
||||||
(let ((drive (get-drive letter)))
|
|
||||||
(when drive
|
|
||||||
(emit 0 letter)
|
|
||||||
(dump-directory-entries 1 (drive-root-directory drive)))))
|
|
||||||
|
|
||||||
(define (handle-entry entry)
|
|
||||||
(let ((path (tar-split-path (cdr (assq 'path entry)))))
|
(let ((path (tar-split-path (cdr (assq 'path entry)))))
|
||||||
(when (null? path)
|
(when (null? path)
|
||||||
(error "blank path in tar file"))
|
(error "blank path in tar file"))
|
||||||
|
@ -237,12 +281,105 @@
|
||||||
(make-fat-file name
|
(make-fat-file name
|
||||||
(cdr (assq 'contents entry)))))))))))))
|
(cdr (assq 'contents entry)))))))))))))
|
||||||
|
|
||||||
(define (main)
|
(define (input)
|
||||||
(let loop ()
|
(let loop ()
|
||||||
(let ((entry (tar-read-entry)))
|
(let ((entry (tar-read-entry)))
|
||||||
(unless (eof-object? entry)
|
(unless (eof-object? entry)
|
||||||
(handle-entry entry)
|
(input-entry entry)
|
||||||
(loop)))))
|
(loop)))))
|
||||||
|
|
||||||
|
(define (build-file-allocation-table drive)
|
||||||
|
(let ((next-free-cluster 0))
|
||||||
|
(define (allocate-clusters! n)
|
||||||
|
(set! next-free-cluster (+ next-free-cluster n)))
|
||||||
|
(let allocate-tree ((directory (drive-root-directory drive)))
|
||||||
|
(for-each (lambda (entry)
|
||||||
|
(cond ((fat-directory? entry)
|
||||||
|
(fat-directory-set-first-cluster!
|
||||||
|
entry next-free-cluster)
|
||||||
|
(let ((entries (fat-directory-entries entry)))
|
||||||
|
(allocate-clusters! (* 32 (length entries)))))
|
||||||
|
((fat-file? entry)
|
||||||
|
(fat-file-set-first-cluster! entry next-free-cluster)
|
||||||
|
(allocate-clusters! (fat-file-clusters entry)))
|
||||||
|
(else
|
||||||
|
(error "What?" entry))))
|
||||||
|
(fat-directory-entries directory))
|
||||||
|
(for-each allocate-tree
|
||||||
|
(filter fat-directory? (fat-directory-entries directory))))))
|
||||||
|
|
||||||
|
(define (ascii-alphanumeric? char)
|
||||||
|
(or (char<=? #\0 char #\9)
|
||||||
|
(char<=? #\A char #\Z)
|
||||||
|
(char<=? #\a char #\z)))
|
||||||
|
|
||||||
|
(define (valid-fat-filename-stem? str)
|
||||||
|
(and (<= (string-length str) 8)
|
||||||
|
(string-every? ascii-alphanumeric? str)))
|
||||||
|
|
||||||
|
(define (valid-fat-filename-extension? str)
|
||||||
|
(and (<= (string-length str) 3)
|
||||||
|
(string-every? ascii-alphanumeric? str)))
|
||||||
|
|
||||||
|
(define (encode-8.3-filename str)
|
||||||
|
(let* ((dot (string-index str #\.))
|
||||||
|
(stem (if dot (string-copy str 0 dot) str))
|
||||||
|
(ext (if dot (string-copy str (+ dot 1) (string-length str)) "")))
|
||||||
|
(unless (valid-fat-filename-stem? stem)
|
||||||
|
(error "stem" stem))
|
||||||
|
(unless (valid-fat-filename-extension? ext)
|
||||||
|
(error "ext" dot ext stem))
|
||||||
|
(if (and (valid-fat-filename-stem? stem)
|
||||||
|
(valid-fat-filename-extension? ext))
|
||||||
|
(string->utf8 (string-append (string-pad stem 8 #\space)
|
||||||
|
(string-pad ext 3 #\space)))
|
||||||
|
(error "Bad 8.3 filename" str))))
|
||||||
|
|
||||||
|
(define (encode-directory-entry entry)
|
||||||
|
(let ((attributes (bitwise-ior #x20 (if (fat-directory? entry) #x10 0))))
|
||||||
|
(bytevector-append
|
||||||
|
(encode-8.3-filename (fat-directory-entry-name entry))
|
||||||
|
(u8 attributes)
|
||||||
|
(u8 0) ; misc non-portable info
|
||||||
|
(u8 0) ; misc non-portable info
|
||||||
|
(u16l 0) ; create time-of-day
|
||||||
|
(u16l 0) ; create date
|
||||||
|
(u16l 0) ; access date
|
||||||
|
(u16l 0) ; misc non-portable info
|
||||||
|
(u16l 0) ; modify time-of-day
|
||||||
|
(u16l 0) ; modify date
|
||||||
|
(u16l (if (fat-file? entry) (fat-file-first-cluster entry) 0))
|
||||||
|
(u32l (if (fat-file? entry) (fat-file-size entry) 0)))))
|
||||||
|
|
||||||
|
(define (write-tree directory)
|
||||||
|
(write-bytevector
|
||||||
|
(bytevector-concatenate
|
||||||
|
(map encode-directory-entry
|
||||||
|
(fat-directory-entries directory))))
|
||||||
|
(for-each write-tree
|
||||||
|
(filter fat-directory? (fat-directory-entries directory))))
|
||||||
|
|
||||||
|
(define (write-drive drive)
|
||||||
|
(write-tree (drive-root-directory drive)))
|
||||||
|
|
||||||
|
(define (display-drive drive)
|
||||||
|
(define (emit nest str)
|
||||||
|
(write-string (make-string (* 2 nest) #\space))
|
||||||
|
(write-string str)
|
||||||
|
(newline))
|
||||||
|
(define (dump-directory-entries nest directory)
|
||||||
|
(for-each (lambda (entry)
|
||||||
|
(emit nest (fat-directory-entry-name entry))
|
||||||
|
(when (fat-directory? entry)
|
||||||
|
(dump-directory-entries (+ nest 1) entry)))
|
||||||
|
(fat-directory-entries directory)))
|
||||||
|
(emit 0 (drive-letter drive))
|
||||||
|
(dump-directory-entries 1 (drive-root-directory drive)))
|
||||||
|
|
||||||
|
(define (main)
|
||||||
|
(input)
|
||||||
|
(for-each build-file-allocation-table (get-used-drives))
|
||||||
|
(for-each display-drive (get-used-drives))
|
||||||
|
(for-each write-drive (get-used-drives)))
|
||||||
|
|
||||||
(main)
|
(main)
|
||||||
(dump-drive "c")
|
|
||||||
|
|
Loading…
Reference in New Issue