diff options
-rw-r--r-- | tools/xor_compress.c | 2 | ||||
-rw-r--r-- | tools/xor_compress.py | 25 | ||||
-rw-r--r-- | tools/xor_compress.rs | 97 |
3 files changed, 119 insertions, 5 deletions
diff --git a/tools/xor_compress.c b/tools/xor_compress.c index f186f05..48e5167 100644 --- a/tools/xor_compress.c +++ b/tools/xor_compress.c @@ -90,7 +90,7 @@ int write_compressed(const char *filename, unsigned char *data, size_t n, bool v } } - if (verbose) fprintf(stderr, PROGRAM_NAME ": %s: ld bc, $%x\n", filename, runs); + if (verbose) printf(PROGRAM_NAME ": %s: ld bc, $%x\n", filename, runs); fflush(f); fclose(f); diff --git a/tools/xor_compress.py b/tools/xor_compress.py index b3bf317..015c80e 100644 --- a/tools/xor_compress.py +++ b/tools/xor_compress.py @@ -1,11 +1,22 @@ #!/usr/bin/env python3 -# Usage: ./xor_compress.py sources.bin... > dest.bin.xor - import sys +sys.argv.pop(0) + +verbose = False +if sys.argv and sys.argv[0] == '-v': + verbose = True + sys.argv.pop(0) + +if len(sys.argv) < 2: + print('Usage: xor_compress.py [-v] file... files.xor', file=sys.stderr) + exit(1) + +out_filename = sys.argv.pop() + data = bytearray() -for filename in sys.argv[1:]: +for filename in sys.argv: with open(filename, 'rb') as f: data.extend(f.read()) @@ -13,10 +24,12 @@ n = len(data) output = bytearray() v = 0x00 i = 0 +runs = 0 while i < n: byte = data[i] i += 1 + runs += 1 if data[i] == v: # Alternating (>= 0x80) @@ -44,4 +57,8 @@ while i < n: output.append(len(buffer) - 1) output.extend(buffer) -sys.stdout.buffer.write(output) +with open(out_filename, 'wb') as f: + f.write(output) + +if verbose: + print('%s: ld bc, $%x' % (out_filename, runs)) diff --git a/tools/xor_compress.rs b/tools/xor_compress.rs new file mode 100644 index 0000000..0d912ba --- /dev/null +++ b/tools/xor_compress.rs @@ -0,0 +1,97 @@ +const PROGRAM_NAME: &str = "xor_compress"; + +fn read_files(filenames: &[String]) -> Result<Vec<u8>, (&String, std::io::Error)> { + let mut data = Vec::new(); + for filename in filenames.iter() { + match std::fs::read(filename) { + Ok(bytes) => data.extend(&bytes), + Err(err) => return Err((filename, err)), + } + } + Ok(data) +} + +fn write_compressed(filename: &String, data: Vec<u8>) -> Result<u32, std::io::Error> { + let n = data.len(); + + let mut output = Vec::new(); + let mut v = 0x00; + let mut i = 0; + let mut runs = 0; + + while i < n { + let mut byte = data[i]; + i += 1; + runs += 1; + + if data[i] == v { + // Alternating (>= 0x80) + // Run stops at 0x80 bytes or when the values stop alternating + let mut size = 0; + while i < n && size < 0x80 && data[i] == (if size % 2 == 0 { v } else { byte }) { + size += 1; + i += 1; + } + output.push(size + 0x7f); + output.push(v ^ byte); + if size % 2 == 0 { + v = byte; + } + } else { + // Sequential (< 0x80) + // Run stops at 0x80 bytes or when the value two ahead is equal to v + let mut buffer = vec![v ^ byte]; + while i < n { + v = byte; + if buffer.len() > 0x7f || (i + 1 < n && data[i + 1] == v) { + break; + } + byte = data[i]; + buffer.push(v ^ byte); + i += 1; + } + output.push((buffer.len() - 1) as u8); + output.extend(buffer); + } + } + + match std::fs::write(filename, &output[..]) { + Ok(()) => Ok(runs), + Err(err) => Err(err), + } +} + +fn main() { + let mut args: Vec<String> = std::env::args().skip(1).collect(); + + let mut verbose = false; + if !args.is_empty() && args[0] == "-v" { + verbose = true; + args.remove(0); + } + + if args.len() < 2 { + eprintln!("Usage: {} [-v] file... files.xor", PROGRAM_NAME); + std::process::exit(1); + } + + let out_filename = args.pop().unwrap(); + let data = match read_files(&args[..]) { + Ok(data) => data, + Err((filename, err)) => { + eprintln!("{}: {}: {}", PROGRAM_NAME, filename, err); + std::process::exit(err.raw_os_error().unwrap_or(1)); + } + }; + if !data.is_empty() { + match write_compressed(&out_filename, data) { + Ok(runs) => if verbose { + println!("{}: {}: ld bc, ${:x}", PROGRAM_NAME, out_filename, runs); + }, + Err(err) => { + eprintln!("{}: {}: {}", PROGRAM_NAME, out_filename, err); + std::process::exit(err.raw_os_error().unwrap_or(1)); + }, + } + } +} |