/ patch_idcode.scm
patch_idcode.scm
1 #!/usr/bin/env -S guile -e "main" -s 2 !# 3 4 ;; -*- geiser-scheme-implementation: guile -*- 5 6 (import (srfi srfi-1) 7 (srfi srfi-26) 8 (rnrs bytevectors) 9 (ice-9 format) 10 (ice-9 binary-ports)) 11 12 ;;; File reading block size 13 (define BLOCK-SIZE 1024) 14 15 ;;; Make backup file 16 (define MAKE-BACKUP #t) 17 18 ;;; Vectors matching 19 (define (bytevector-match va vb skew) 20 (let ((alen (bytevector-length va)) 21 (blen (bytevector-length vb))) 22 (let match-rec ((pos 0)) 23 (if (>= pos blen) 24 #t 25 (if (or (>= (+ pos skew) alen) 26 (< (+ pos skew) 0) 27 (not 28 (eq? (bytevector-u8-ref va (+ pos skew)) 29 (bytevector-u8-ref vb pos)))) 30 #f 31 (match-rec (+ pos 1))))))) 32 33 ;;; Find pattern in bytevector 34 (define (bytevector-find v pattern) 35 (let find-rec ((pos 0)) 36 (if (>= pos (bytevector-length v)) 37 #f 38 (if (bytevector-match v pattern pos) 39 pos 40 (find-rec (+ pos 1)))))) 41 42 ;;; Find binary sequence in file 43 (define (find-binary-sequence port seq current-position) 44 (let ((seqlen (bytevector-length seq))) 45 (let find-rec ((pos current-position)) 46 (let ((data (get-bytevector-n port BLOCK-SIZE))) 47 (if (eof-object? data) 48 #f 49 (let ((match (bytevector-find data seq))) 50 (if match 51 (+ pos match) 52 (if (< (bytevector-length data) BLOCK-SIZE) 53 #f 54 (begin 55 (unget-bytevector port data (- BLOCK-SIZE seqlen) seqlen) 56 (find-rec (- (+ pos BLOCK-SIZE) seqlen))))))))))) 57 58 ;;; Patch binary file 59 (define* (patch-file! file pos seq #:key (backup #f)) 60 (let ((port (open-file file "r+"))) 61 (seek port pos SEEK_SET) 62 (when backup 63 (copy-file file (string-append file ".bak"))) 64 (seek port pos SEEK_SET) 65 (put-bytevector port seq) 66 (close-port port))) 67 68 ;;; Return byte n of the number x 69 (define (byte-ref x n) 70 (bit-extract x (* n 8) (* (+ 1 n) 8))) 71 72 ;;; Convert number to bytes 73 (define (uint->bytevector uint) 74 (let ((bcount (ceiling (/ (integer-length uint) 8)))) 75 (u8-list->bytevector 76 (map (lambda (n) (byte-ref uint n)) (reverse (iota bcount)))))) 77 78 ;;; MAIN 79 (define (main args) 80 (if (< (length args) 4) 81 (begin 82 (format (current-error-port) "Usage: ~a <find-id-code-hex> <replace-id-code-hex> <bitstream.bit>\n" (first args)) 83 (format (current-error-port) "Example: ~a deadbeef defec8ed blink.bit\n" (first args))) 84 85 (let ((find (uint->bytevector (string->number (list-ref args 1) 16))) 86 (replace (uint->bytevector (string->number (list-ref args 2) 16))) 87 (file (list-ref args 3))) 88 89 (let ((pos (call-with-input-file file 90 (cut find-binary-sequence <> find 0) 91 #:binary #t))) 92 (if pos 93 (begin 94 (format #t "Pattern is found at position ~a. Replace.\n" pos) 95 (patch-file! file pos replace #:backup MAKE-BACKUP)) 96 (format #t "Pattern is not found. Abort.\n"))))))