From b02f67aef5c772dd47cb2756886626b795a584ec Mon Sep 17 00:00:00 2001 From: Ivan Tatarinov Date: Mon, 12 Apr 2021 14:02:10 +0300 Subject: [PATCH] sdk: moved `hardware/kartusho/roms/zx7b.exe` to `sdk/bin/zx7b.exe`, added sources --- .gitignore | 3 + sdk/Makefile | 5 +- {hardware/kartusho/roms => sdk/bin}/zx7b.exe | Bin sdk/bin/zx7b.exe.license | 13 + sdk/src/Makefile | 16 +- sdk/src/zx7b/Makefile | 31 ++ sdk/src/zx7b/zx7b.c | 321 +++++++++++++++++++ 7 files changed, 385 insertions(+), 4 deletions(-) rename {hardware/kartusho/roms => sdk/bin}/zx7b.exe (100%) create mode 100644 sdk/bin/zx7b.exe.license create mode 100644 sdk/src/zx7b/Makefile create mode 100644 sdk/src/zx7b/zx7b.c diff --git a/.gitignore b/.gitignore index 441d41d..e67c4e0 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,7 @@ cores/Oric/source/_impact.log cores/Oric/build/oric.v4_my.bit sdk/bin/sjasmplus +sdk/bin/zx7b sdk/src/sjasmplus +sdk/src/zx7b/zx7b +sdk/src/zx7b/zx7b.exe diff --git a/sdk/Makefile b/sdk/Makefile index 3b82f73..3d04af9 100644 --- a/sdk/Makefile +++ b/sdk/Makefile @@ -10,12 +10,15 @@ include common.mk ifeq ($(OS),Windows_NT) SJASMPLUS := sjasmplus.exe +ZX7B := zx7b.exe else SJASMPLUS := sjasmplus +ZX7B := zx7b endif TOOLS:=\ - bin/$(SJASMPLUS) + bin/$(SJASMPLUS) \ + bin/$(ZX7B) .PHONY: all all: $(TOOLS) diff --git a/hardware/kartusho/roms/zx7b.exe b/sdk/bin/zx7b.exe similarity index 100% rename from hardware/kartusho/roms/zx7b.exe rename to sdk/bin/zx7b.exe diff --git a/sdk/bin/zx7b.exe.license b/sdk/bin/zx7b.exe.license new file mode 100644 index 0000000..88d00ee --- /dev/null +++ b/sdk/bin/zx7b.exe.license @@ -0,0 +1,13 @@ +SPDX-FileName: zx7b.exe + +SPDX-FileType: BINARY + +SPDX-FileChecksum: SHA1: de47984d9d155eb89153052297caac5ffef5e720 + +SPDX-LicenseConcluded: BSD-3-Clause + +SPDX-LicenseComments: The concluded license is taken from https://github.com/antoniovillena/zx7b/raw/master/zx7b.c + +SPDX-FileCopyrightText: Einar Saukas/AntonioVillena, 28 Dec 2013 + +SPDX-FileComment: ZX7 Backwards compressor v1.01 by Einar Saukas/AntonioVillena, 28 Dec 2013 diff --git a/sdk/src/Makefile b/sdk/src/Makefile index 6c8cf29..62b8865 100644 --- a/sdk/src/Makefile +++ b/sdk/src/Makefile @@ -10,13 +10,16 @@ include ../common.mk ifeq ($(OS),Windows_NT) SJASMPLUS := sjasmplus.exe +ZX7B := zx7b.exe else SJASMPLUS := sjasmplus +ZX7B := zx7b endif .PHONY: all all: \ - ../bin/$(SJASMPLUS) + ../bin/$(SJASMPLUS) \ + ../bin/$(ZX7B) ifneq ($(OS),Windows_NT) ../bin/$(SJASMPLUS): sjasmplus/build/$(SJASMPLUS) @@ -33,13 +36,20 @@ sjasmplus: tar -xzf sjasmplus.tgz mv sjasmplus-20190306.1 sjasmplus rm -f sjasmplus.tgz + +../bin/$(ZX7B): zx7b/$(ZX7B) + cp $< $@ + +zx7b/$(ZX7B): | zx7b + $(MAKE) -w -C $| endif .PHONY: clean ifeq ($(OS),Windows_NT) clean:; else -clean: | sjasmplus sjasmplus.mk +clean: | sjasmplus sjasmplus.mk zx7b $(MAKE) -w -C sjasmplus -f ../sjasmplus.mk clean - rm -f ../bin/$(SJASMPLUS) + $(MAKE) -w -C zx7b clean + rm -f ../bin/$(SJASMPLUS) ../bin/$(ZX7B) endif diff --git a/sdk/src/zx7b/Makefile b/sdk/src/zx7b/Makefile new file mode 100644 index 0000000..9a92b79 --- /dev/null +++ b/sdk/src/zx7b/Makefile @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2021 Ivan Tatarinov +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Supported environments: +# * GNU on Linux, FreeBSD etc. +# * GNU on Windows NT (using MinGW/MSYS/Cygwin/WSL) + +include ../../common.mk + +ifeq ($(OS),Windows_NT) +ZX7B := zx7b.exe +else +ZX7B := zx7b +endif + +ifneq ($(OS),Windows_NT) +$(ZX7B): zx7b.c + $(CC) $(CFLAGS) -o $@ $< + +zx7b.c: + wget -c https://github.com/antoniovillena/zx7b/raw/master/zx7b.c +endif + +.PHONY: clean +ifeq ($(OS),Windows_NT) +clean:; +else +clean: + rm -f $(ZX7B) +endif diff --git a/sdk/src/zx7b/zx7b.c b/sdk/src/zx7b/zx7b.c new file mode 100644 index 0000000..9a63080 --- /dev/null +++ b/sdk/src/zx7b/zx7b.c @@ -0,0 +1,321 @@ +/* + * ZX7b (c) Copyright 2013 by Antonio Villena. All rights reserved. + * + * Based on ZX7 + * (c) Copyright 2012 by Einar Saukas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The name of its author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * SPDX-FileCopyrightText: ZX7b (c) Copyright 2013 by Antonio Villena. All rights reserved. + * + * SPDX-FileNotice: Based on ZX7 + * SPDX-FileNotice: (c) Copyright 2012 by Einar Saukas. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * SPDX-LicenseComments: License's text is equals to one from https://directory.fsf.org/wiki/License:BSD-3-Clause + */ + +#include +#include +#include + +#define MAX_OFFSET 2176 /* range 1..2176 */ +#define MAX_LEN 65536 /* range 2..65536 */ + +typedef struct match_t { + size_t index; + struct match_t *next; +} Match; + +typedef struct optimal_t { + size_t bits; + int offset; + int len; +} Optimal; + +Optimal *optimize(unsigned char *input_data, size_t input_size); + +unsigned char *compress(Optimal *optimal, unsigned char *input_data, size_t input_size, size_t *output_size); + +int main(int argc, char *argv[]) { + FILE *ifp; + FILE *ofp; + unsigned char *input_data; + unsigned char *output_data; + size_t input_size; + size_t output_size; + size_t partial_counter; + size_t total_counter; + char *output_name; + int i, j; + + if( argc==1 ) + printf("\nZX7 Backwards compressor v1.01 by Einar Saukas/AntonioVillena, 28 Dec 2013\n\n" + " zx7b \n\n" + " Raw input file\n" + " Compressed output file\n\n" + "Example: zx7b Cobra.scr Cobra.zx7b\n"), + exit(0); + if( argc!=3 ) + printf("\nInvalid number of parameters\n"), + exit(-1); + + /* open input file */ + ifp= fopen(argv[1], "rb"); + if( !ifp ) + fprintf(stderr, "Error: Cannot access input file %s\n", argv[1]), + exit(1); + + /* determine input size */ + fseek(ifp, 0L, SEEK_END); + input_size= ftell(ifp); + fseek(ifp, 0L, SEEK_SET); + if( !input_size ) + fprintf(stderr, "Error: Empty input file %s\n", argv[1]), + exit(1); + + /* allocate input buffer */ + input_data= (unsigned char *)malloc(input_size); + if( !input_data ) + fprintf(stderr, "Error: Insufficient memory\n"), + exit(1); + + /* read input file */ + total_counter= 0; + do { + partial_counter = fread(input_data+total_counter, sizeof(char), input_size-total_counter, ifp); + total_counter += partial_counter; + } while ( partial_counter > 0 ); + + if( total_counter != input_size ) + fprintf(stderr, "Error: Cannot read input file %s\n", argv[1]), + exit(1); + + /* close input file */ + fclose(ifp); + + /* create output file */ + ofp= fopen(argv[2], "wb"); + if( !ofp ) + fprintf(stderr, "Error: Cannot create output file %s\n", argv[2]), + exit(1); + + for ( i= 0; i>1; i++ ) + j= input_data[i], + input_data[i]= input_data[input_size-1-i], + input_data[input_size-1-i]= j; + + /* generate output file */ + output_data= compress(optimize(input_data, input_size), input_data, input_size, &output_size); + + for ( i= 0; i>1; i++ ) + j= output_data[i], + output_data[i]= output_data[output_size-1-i], + output_data[output_size-1-i]= j; + + /* write output file */ + if( fwrite(output_data, sizeof(char), output_size, ofp) != output_size ) + fprintf(stderr, "Error: Cannot write output file %s\n", output_name), + exit(1); + + /* close output file */ + fclose(ofp); + + /* done! */ + printf("\nFile %s compressed from %s (%d to %d bytes)\n", argv[2], argv[1], (int) input_size, (int) output_size); + return 0; +} + +unsigned char* output_data; +size_t output_index; +size_t bit_index; +int bit_mask; + +void write_byte(int value) { + output_data[output_index++] = value; +} + +void write_bit(int value) { + if( bit_mask == 0 ) + bit_mask = 128, + bit_index = output_index, + write_byte(0); + if( value > 0 ) + output_data[bit_index] |= bit_mask; + bit_mask >>= 1; +} + +int elias_gamma_bits(int value) { + int bits; + bits= 1; + while ( value > 1 ) + bits+= 2, + value>>= 1; + return bits; +} + +void write_elias_gamma(int value) { + int bits= 0, rvalue= 0; + while ( value>1 ) + ++bits, + rvalue<<= 1, + rvalue|= value&1, + value>>= 1; + while ( bits-- ) + write_bit(0), + write_bit(rvalue & 1), + rvalue>>= 1; + write_bit(1); +} + +unsigned char *compress(Optimal *optimal, unsigned char *input_data, size_t input_size, size_t *output_size) { + size_t input_index; + size_t input_prev; + int offset1; + int mask; + int i; + + /* calculate and allocate output buffer */ + input_index= input_size-1; + *output_size= (optimal[input_index].bits+16+7)/8; + output_data= (unsigned char *)malloc(*output_size); + if( !output_data ) + fprintf(stderr, "Error: Insufficient memory\n"), + exit(1); + + /* un-reverse optimal sequence */ + optimal[input_index].bits= 0; + while ( input_index > 0 ) + input_prev= input_index - (optimal[input_index].len > 0 ? optimal[input_index].len : 1), + optimal[input_prev].bits= input_index, + input_index= input_prev; + + output_index= 0; + bit_mask= 0; + + /* first byte is always literal */ + write_byte(input_data[0]); + + /* process remaining bytes */ + while ( (input_index= optimal[input_index].bits) > 0) + if( optimal[input_index].len == 0) + write_bit(0), + write_byte(input_data[input_index]); + else{ + /* sequence indicator */ + write_bit(1); + + /* sequence length */ + write_elias_gamma(optimal[input_index].len-1); + + /* sequence offset */ + offset1= optimal[input_index].offset-1; + if( offset1 < 128 ) + write_byte(offset1); + else{ + offset1-= 128; + write_byte((offset1 & 127) | 128); + for ( mask= 1024; mask > 127; mask >>= 1) + write_bit(offset1 & mask); + } + } + + /* end mark */ + write_bit(1); + write_elias_gamma(0xff); + return output_data; +} + +int count_bits(int offset, int len) { + return 1 + (offset > 128 ? 12 : 8) + elias_gamma_bits(len-1); +} + +Optimal* optimize(unsigned char *input_data, size_t input_size) { + size_t *min; + size_t *max; + Match *matches; + Match *match_slots; + Optimal *optimal; + Match *match; + int match_index; + int offset; + size_t len; + size_t best_len; + size_t bits; + size_t i; + + /* allocate all data structures at once */ + min = (size_t *)calloc(MAX_OFFSET+1, sizeof(size_t)); + max = (size_t *)calloc(MAX_OFFSET+1, sizeof(size_t)); + matches = (Match *)calloc(256*256, sizeof(Match)); + match_slots = (Match *)calloc(input_size, sizeof(Match)); + optimal = (Optimal *)calloc(input_size, sizeof(Optimal)); + + if (!min || !max || !matches || !match_slots || !optimal) { + fprintf(stderr, "Error: Insufficient memory\n"); + exit(1); + } + + /* first byte is always literal */ + optimal[0].bits = 8; + + /* process remaining bytes */ + for ( i= 1; i < input_size; i++ ){ + optimal[i].bits= optimal[i-1].bits + 9; + match_index= input_data[i-1] << 8 | input_data[i]; + best_len= 1; + for ( match= &matches[match_index]; match->next != NULL && best_len < MAX_LEN; match = match->next){ + offset= i - match->next->index; + if( offset > MAX_OFFSET ){ + match->next = NULL; + break; + } + for ( len= 2; len <= MAX_LEN; len++ ){ + if( len > best_len && len&0xff ){ + best_len= len; + bits= optimal[i-len].bits + count_bits(offset, len); + if( optimal[i].bits > bits ) + optimal[i].bits= bits, + optimal[i].offset= offset, + optimal[i].len= len; + } + else if ( i+1 == max[offset]+len && max[offset] != 0 ){ + len= i-min[offset]; + if( len > best_len ) + len= best_len; + } + if( i < offset+len || input_data[i-len] != input_data[i-len-offset] ) + break; + } + min[offset]= i+1-len; + max[offset]= i; + } + match_slots[i].index= i; + match_slots[i].next= matches[match_index].next; + matches[match_index].next= &match_slots[i]; + } + + /* save time by releasing the largest block only, the O.S. will clean everything else later */ + free(match_slots); + return optimal; +}