gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
commit 778f2339757098179a940554af031546bbba83e9 parent 0f5160316f8966d01030083b2ff5f95e69c0d73a Author: Toxa <untoxa@mail.ru> Date: Sun, 23 Nov 2025 02:11:29 +0300 Merge branch 'develop' of https://github.com/gbdk-2020/gbdk-2020 into develop Diffstat:
190 files changed, 7598 insertions(+), 240 deletions(-)
diff --git a/LICENSE b/LICENSE @@ -11,6 +11,10 @@ Q: What license information is required when distributing the compiled ROM (bina A: There is no requirement to include or credit any of the GBDK-2020 licenses or authors, although credit of GBDK-2020 is appreciated. This is different and separate from redistributing the GBDK-2020 dev environment itself (or the GBDK-2020 sources) which does require the licenses. +If zx0 decompression from the GBDK library is used then credit should be included per the requirements of it's BSD 3-Clause license. See the LICENSE folder for details. + + + ### GBDK Library & Makefiles, SDCC: GPLv2+LE (linking exception). Some files from SDCC, some from the historical GBDK that were originally LGPL and have been relicensed to GPLv2+LE (linking exception) to match the SDCC ones in 2022 by permission of original authors. @@ -35,6 +39,14 @@ According to: http://sdcc.sourceforge.net/ Public Domain. @bbbbbr is the original author +### zx0 compression in gbcompress +The zx0 salvador code is available under the Zlib license, except for matchfinder.c which is placed under the Creative Commons CC0 license. +Please consult LICENSE.zlib.md and LICENSE.CC0.md for more information. + +### zx0 compression in the library +BSD 3-Clause, Einar Saukas + + ### png2asset: MIT License. @Zal0 is the original author + additional contributors @@ -76,4 +88,4 @@ To that end: Great! We've got the required signoff so I'll start the repo changes and we're good to go. -Thank you so much Michael, Pascal and Jon for allowing the re-license. We really appreciate the time you've taken to help resolve this issue and clear up any license ambiguity. - +Thank you so much Michael, Pascal and Jon for allowing the re-license. We really appreciate the time you've taken to help resolve this issue and clear up any license ambiguity. diff --git a/gbdk-support/gbcompress/Makefile b/gbdk-support/gbcompress/Makefile @@ -18,11 +18,23 @@ endif CC = $(TOOLSPREFIX)gcc CFLAGS = -ggdb -O -Wno-incompatible-pointer-types -DGBDKLIBDIR=\"$(TARGETDIR)\" OBJ = main.o gbcompress.o rlecompress.o files.o files_c_source.o +ZX_OBJ = zx0_expand.o zx0_matchfinder.o zx0_shrink.o +ZX_LIB_OBJ = trsort.o divsufsort.o divsufsort_utils.o sssort.o BIN = gbcompress + all: $(BIN) -$(BIN): $(OBJ) +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +%.o: zx0/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +%.o: zx0/libdivsufsort/%.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(BIN): $(OBJ) $(ZX_OBJ) $(ZX_LIB_OBJ) clean: rm -f *.o $(BIN) *~ @@ -32,23 +44,37 @@ clean: # round trip the executable through compression and de-compression as a brief test test: # Compression test on the executable itself + @echo "----- Test: GB Compress -----" rm -f tmp.cmp; rm -f tmp.dcmp cp $(BIN) tmp.in; ./gbcompress -v tmp.in tmp.cmp; ./gbcompress -v -d tmp.cmp tmp.dcmp; diff -s tmp.in tmp.dcmp rm -f tmp.cmp.c; rm -f tmp.dcmp.c; rm -f tmp.cmp; rm -f tmp.dcmp cp $(BIN) tmp.in; ./gbcompress -v --cout --varname=some_array tmp.in tmp.cmp.c; ./gbcompress -v -d --cin tmp.cmp.c tmp.dcmp; diff -s tmp.in tmp.dcmp + @echo "----- Test: RLE Compress -----" rm tmp.* rm -f tmp.cmp; rm -f tmp.dcmp cp $(BIN) tmp.in; ./gbcompress --alg=rle -v tmp.in tmp.cmp; ./gbcompress --alg=rle -v -d tmp.cmp tmp.dcmp; diff -s tmp.in tmp.dcmp rm -f tmp.cmp.c; rm -f tmp.dcmp.c; rm -f tmp.cmp; rm -f tmp.dcmp cp $(BIN) tmp.in; ./gbcompress --alg=rle -v --cout --varname=some_array tmp.in tmp.cmp.c; ./gbcompress --alg=rle -v -d --cin tmp.cmp.c tmp.dcmp; diff -s tmp.in tmp.dcmp + @echo "----- Test: ZX0 Compress -----" + rm tmp.* + rm -f tmp.cmp; rm -f tmp.dcmp + cp $(BIN) tmp.in; ./gbcompress --alg=zx0 -v tmp.in tmp.cmp; ./gbcompress --alg=zx0 -v -d tmp.cmp tmp.dcmp; diff -s tmp.in tmp.dcmp + rm -f tmp.cmp.c; rm -f tmp.dcmp.c; rm -f tmp.cmp; rm -f tmp.dcmp + cp $(BIN) tmp.in; ./gbcompress --alg=zx0 -v --cout --varname=some_array tmp.in tmp.cmp.c; ./gbcompress --alg=zx0 -v -d --cin tmp.cmp.c tmp.dcmp; diff -s tmp.in tmp.dcmp rm -f tmp.* # test_no_u16_align_end_of_buf.c + @echo "" + @echo "----- Test: GB Compress -----" rm -f tmp.* - cp test_data/test_no_u16_align_end_of_buf.c tmp.in.c; ./gbcompress --cin -v --cout tmp.in.c tmp.cmp.c; ./gbcompress -v -d --cin --cout tmp.cmp.c tmp.dcmp.c; diff -s tmp.in.c tmp.dcmp.c + cp test_data/test_no_u16_align_end_of_buf.c tmp.in.c; ./gbcompress --cin -v --cout tmp.in.c tmp.cmp.c; ./gbcompress -v -d --cin --cout tmp.cmp.c tmp.dcmp.c; diff --ignore-blank-lines --ignore-space-change -s tmp.in.c tmp.dcmp.c + @echo "----- Test: RLE Compress -----" rm -f tmp.* - cp test_data/test_no_u16_align_end_of_buf.c tmp.in.c; ./gbcompress --alg=rle --cin -v --cout tmp.in.c tmp.cmp.c; ./gbcompress --alg=rle -v -d --cin --cout tmp.cmp.c tmp.dcmp.c; diff -s tmp.in.c tmp.dcmp.c + cp test_data/test_no_u16_align_end_of_buf.c tmp.in.c; ./gbcompress --alg=rle --cin -v --cout tmp.in.c tmp.cmp.c; ./gbcompress --alg=rle -v -d --cin --cout tmp.cmp.c tmp.dcmp.c; diff --ignore-blank-lines --ignore-space-change -s tmp.in.c tmp.dcmp.c + rm -f tmp.* + @echo "----- Test: ZX0 Compress -----" + rm -f tmp.* + cp test_data/test_no_u16_align_end_of_buf.c tmp.in.c; ./gbcompress --alg=zx0 --cin -v --cout tmp.in.c tmp.cmp.c; ./gbcompress --alg=zx0 -v -d --cin --cout tmp.cmp.c tmp.dcmp.c; diff --ignore-blank-lines --ignore-space-change -s tmp.in.c tmp.dcmp.c rm -f tmp.* - diff --git a/gbdk-support/gbcompress/main.c b/gbdk-support/gbcompress/main.c @@ -14,10 +14,13 @@ #include "files.h" #include "files_c_source.h" +#include "zx0/zx0_libsalvador.h" + #define MAX_STR_LEN 4096 #define COMPRESSION_TYPE_GB 0 #define COMPRESSION_TYPE_RLE_BLOCK 1 +#define COMPRESSION_TYPE_ZX0 2 #define COMPRESSION_TYPE_DEFAULT COMPRESSION_TYPE_GB char filename_in[MAX_STR_LEN] = {'\0'}; @@ -28,7 +31,7 @@ uint8_t * p_buf_out = NULL; bool opt_mode_compress = true; bool opt_verbose = false; -bool opt_compression_type = COMPRESSION_TYPE_DEFAULT; +int opt_compression_type = COMPRESSION_TYPE_DEFAULT; bool opt_c_source_input = false; bool opt_c_source_output = false; char opt_c_source_output_varname[MAX_STR_LEN] = "var_name"; @@ -53,7 +56,7 @@ static void display_help(void) { "--cin : Read input as .c source format (8 bit char ONLY, uses first array found)\n" "--cout : Write output in .c / .h source format (8 bit char ONLY) \n" "--varname=<NAME> : specify variable name for c source output\n" - "--alg=<type> : specify compression type: 'rle', 'gb' (default)\n" + "--alg=<type> : specify compression type: 'zx0', 'rle', 'gb' (default)\n" "--bank=<num> : Add Bank Ref: %d - %d (default is none, with --cout only)\n" "Example: \"gbcompress binaryfile.bin compressed.bin\"\n" "Example: \"gbcompress -d compressedfile.bin decompressed.bin\"\n" @@ -95,6 +98,8 @@ int handle_args(int argc, char * argv[]) { opt_compression_type = COMPRESSION_TYPE_GB; } else if (strstr(argv[i], "--alg=rle") == argv[i]) { opt_compression_type = COMPRESSION_TYPE_RLE_BLOCK; + } else if (strstr(argv[i], "--alg=zx0") == argv[i]) { + opt_compression_type = COMPRESSION_TYPE_ZX0; } else if (strstr(argv[i], "-d") == argv[i]) { opt_mode_compress = false; } else if (strstr(argv[i], "--bank=") == argv[i]) { @@ -119,7 +124,6 @@ int handle_args(int argc, char * argv[]) { } } - return false; } @@ -153,13 +157,20 @@ static int compress() { buf_size_out = buf_size_in; p_buf_out = malloc(buf_size_out); - if ((p_buf_in) && (p_buf_out) && (buf_size_in > 0)) { + if (!p_buf_out) return EXIT_FAILURE; + + + if ((p_buf_in) && (buf_size_in > 0)) { if (opt_compression_type == COMPRESSION_TYPE_GB) out_len = gbcompress_buf(p_buf_in, buf_size_in, &p_buf_out, buf_size_out); else if (opt_compression_type == COMPRESSION_TYPE_RLE_BLOCK) out_len = rlecompress_buf(p_buf_in, buf_size_in, &p_buf_out, buf_size_out); - else + else if (opt_compression_type == COMPRESSION_TYPE_ZX0) { + // No Flags, no offset, no dictionary, no progress callback, no stats + memset(p_buf_out, 0, buf_size_out); + out_len = salvador_compress(p_buf_in, p_buf_out, buf_size_in, buf_size_out, 0, 0, 0, NULL, NULL); + } else return EXIT_FAILURE; if (out_len > 0) { @@ -197,7 +208,11 @@ static int decompress() { // Allocate buffer output buffer 3x size of input // It can grow more in gbdecompress_buf() - buf_size_out = buf_size_in * 3; + if (opt_compression_type == COMPRESSION_TYPE_ZX0) + buf_size_out = salvador_get_max_decompressed_size(p_buf_in, buf_size_in, 0); // No flags + else + buf_size_out = buf_size_in * 3; + p_buf_out = malloc(buf_size_out); if ((p_buf_in) && (p_buf_out) && (buf_size_in > 0)) { @@ -206,7 +221,10 @@ static int decompress() { out_len = gbdecompress_buf(p_buf_in, buf_size_in, &p_buf_out, buf_size_out); else if (opt_compression_type == COMPRESSION_TYPE_RLE_BLOCK) out_len = rledecompress_buf(p_buf_in, buf_size_in, &p_buf_out, buf_size_out); - else + else if (opt_compression_type == COMPRESSION_TYPE_ZX0) { + memset(p_buf_out, 0, buf_size_out); + out_len = salvador_decompress(p_buf_in, p_buf_out, buf_size_in, buf_size_out, 0, 0); // No dictionary, no flags + } else return EXIT_FAILURE; if (out_len > 0) { diff --git a/gbdk-support/gbcompress/zx0/libdivsufsort/divsufsort.c b/gbdk-support/gbcompress/zx0/libdivsufsort/divsufsort.c @@ -0,0 +1,431 @@ +/* + * divsufsort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" +#ifdef _OPENMP +# include <omp.h> +#endif + + +/*- Private Functions -*/ + +/* Sorts suffixes of type B*. */ +static +saidx_t +sort_typeBstar(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n) { + saidx_t *PAb, *ISAb, *buf; +#ifdef _OPENMP + saidx_t *curbuf; + saidx_t l; +#endif + saidx_t i, j, k, t, m, bufsize; + saint_t c0, c1; +#ifdef _OPENMP + saint_t d0, d1; + int tmp; +#endif + + /* Initialize bucket arrays. */ + for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; } + for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; } + + /* Count the number of occurrences of the first one or two characters of each + type A, B and B* suffix. Moreover, store the beginning position of all + type B* suffixes into the array SA. */ + for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) { + /* type A suffix. */ + do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1)); + if(0 <= i) { + /* type B* suffix. */ + ++BUCKET_BSTAR(c0, c1); + SA[--m] = i; + /* type B suffix. */ + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { + ++BUCKET_B(c0, c1); + } + } + } + m = n - m; +/* +note: + A type B* suffix is lexicographically smaller than a type B suffix that + begins with the same first two characters. +*/ + + /* Calculate the index of start/end point of each bucket. */ + for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) { + t = i + BUCKET_A(c0); + BUCKET_A(c0) = i + j; /* start point */ + i = t + BUCKET_B(c0, c0); + for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) { + j += BUCKET_BSTAR(c0, c1); + BUCKET_BSTAR(c0, c1) = j; /* end point */ + i += BUCKET_B(c0, c1); + } + } + + if(0 < m) { + /* Sort the type B* suffixes by their first two characters. */ + PAb = SA + n - m; ISAb = SA + m; + for(i = m - 2; 0 <= i; --i) { + t = PAb[i], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = i; + } + t = PAb[m - 1], c0 = T[t], c1 = T[t + 1]; + SA[--BUCKET_BSTAR(c0, c1)] = m - 1; + + /* Sort the type B* substrings using sssort. */ +#ifdef _OPENMP + tmp = omp_get_max_threads(); + buf = SA + m, bufsize = (n - (2 * m)) / tmp; + c0 = ALPHABET_SIZE - 2, c1 = ALPHABET_SIZE - 1, j = m; +#pragma omp parallel default(shared) private(curbuf, k, l, d0, d1, tmp) + { + tmp = omp_get_thread_num(); + curbuf = buf + tmp * bufsize; + k = 0; + for(;;) { + #pragma omp critical(sssort_lock) + { + if(0 < (l = j)) { + d0 = c0, d1 = c1; + do { + k = BUCKET_BSTAR(d0, d1); + if(--d1 <= d0) { + d1 = ALPHABET_SIZE - 1; + if(--d0 < 0) { break; } + } + } while(((l - k) <= 1) && (0 < (l = k))); + c0 = d0, c1 = d1, j = k; + } + } + if(l == 0) { break; } + sssort(T, PAb, SA + k, SA + l, + curbuf, bufsize, 2, n, *(SA + k) == (m - 1)); + } + } +#else + buf = SA + m, bufsize = n - (2 * m); + for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { + for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { + i = BUCKET_BSTAR(c0, c1); + if(1 < (j - i)) { + sssort(T, PAb, SA + i, SA + j, + buf, bufsize, 2, n, *(SA + i) == (m - 1)); + } + } + } +#endif + + /* Compute ranks of type B* substrings. */ + for(i = m - 1; 0 <= i; --i) { + if(0 <= SA[i]) { + j = i; + do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i])); + SA[i + 1] = i - j; + if(i <= 0) { break; } + } + j = i; + do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0); + ISAb[SA[i]] = j; + } + + /* Construct the inverse suffix array of type B* suffixes using trsort. */ + trsort(ISAb, SA, m, 1); + + /* Set the sorted order of tyoe B* suffixes. */ + for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) { + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { } + if(0 <= i) { + t = i; + for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { } + SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t; + } + } + + /* Calculate the index of start/end point of each bucket. */ + BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */ + for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) { + i = BUCKET_A(c0 + 1) - 1; + for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) { + t = i - BUCKET_B(c0, c1); + BUCKET_B(c0, c1) = i; /* end point */ + + /* Move all type B* suffixes to the correct position. */ + for(i = t, j = BUCKET_BSTAR(c0, c1); + j <= k; + --i, --k) { SA[i] = SA[k]; } + } + BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */ + BUCKET_B(c0, c0) = i; /* end point */ + } + } + + return m; +} + +/* Constructs the suffix array by using the sorted order of type B* suffixes. */ +static +void +construct_SA(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n, saidx_t m) { + saidx_t *i, *j, *k; + saidx_t s; + saint_t c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + *j = ~s; + c0 = T[--s]; + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else { + assert(((s == 0) && (T[s] == c1)) || (s < 0)); + *j = ~s; + } + } + } + } + + /* Construct the suffix array by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + if((s == 0) || (T[s - 1] < c0)) { s = ~s; } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else { + assert(s < 0); + *i = ~s; + } + } +} + +#if 0 +/* Constructs the burrows-wheeler transformed string directly + by using the sorted order of type B* suffixes. */ +static +saidx_t +construct_BWT(const sauchar_t *T, saidx_t *SA, + saidx_t *bucket_A, saidx_t *bucket_B, + saidx_t n, saidx_t m) { + saidx_t *i, *j, *k, *orig; + saidx_t s; + saint_t c0, c1, c2; + + if(0 < m) { + /* Construct the sorted order of type B suffixes by using + the sorted order of type B* suffixes. */ + for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { + /* Scan the suffix array from right to left. */ + for(i = SA + BUCKET_BSTAR(c1, c1 + 1), + j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; + i <= j; + --j) { + if(0 < (s = *j)) { + assert(T[s] == c1); + assert(((s + 1) < n) && (T[s] <= T[s + 1])); + assert(T[s - 1] <= T[s]); + c0 = T[--s]; + *j = ~((saidx_t)c0); + if((0 < s) && (T[s - 1] > c0)) { s = ~s; } + if(c0 != c2) { + if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } + k = SA + BUCKET_B(c2 = c0, c1); + } + assert(k < j); + *k-- = s; + } else if(s != 0) { + *j = ~s; +#ifndef NDEBUG + } else { + assert(T[s] == c1); +#endif + } + } + } + } + + /* Construct the BWTed string by using + the sorted order of type B suffixes. */ + k = SA + BUCKET_A(c2 = T[n - 1]); + *k++ = (T[n - 2] < c2) ? ~((saidx_t)T[n - 2]) : (n - 1); + /* Scan the suffix array from left to right. */ + for(i = SA, j = SA + n, orig = SA; i < j; ++i) { + if(0 < (s = *i)) { + assert(T[s - 1] >= T[s]); + c0 = T[--s]; + *i = c0; + if((0 < s) && (T[s - 1] < c0)) { s = ~((saidx_t)T[s - 1]); } + if(c0 != c2) { + BUCKET_A(c2) = k - SA; + k = SA + BUCKET_A(c2 = c0); + } + assert(i < k); + *k++ = s; + } else if(s != 0) { + *i = ~s; + } else { + orig = i; + } + } + + return orig - SA; +} +#endif + +/*---------------------------------------------------------------------------*/ + +/** + * Initialize suffix array context + * + * @return 0 for success, or non-zero in case of an error + */ +int divsufsort_init(divsufsort_ctx_t *ctx) { + ctx->bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); + ctx->bucket_B = NULL; + + if (ctx->bucket_A) { + ctx->bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); + + if (ctx->bucket_B) + return 0; + } + + divsufsort_destroy(ctx); + return -1; +} + +/** + * Destroy suffix array context + * + * @param ctx suffix array context to destroy + */ +void divsufsort_destroy(divsufsort_ctx_t *ctx) { + if (ctx->bucket_B) { + free(ctx->bucket_B); + ctx->bucket_B = NULL; + } + + if (ctx->bucket_A) { + free(ctx->bucket_A); + ctx->bucket_A = NULL; + } +} + +/*- Function -*/ + +saint_t +divsufsort_build_array(divsufsort_ctx_t *ctx, const sauchar_t *T, saidx_t *SA, saidx_t n) { + saidx_t m; + saint_t err = 0; + + /* Check arguments. */ + if((T == NULL) || (SA == NULL) || (n < 0)) { return -1; } + else if(n == 0) { return 0; } + else if(n == 1) { SA[0] = 0; return 0; } + else if(n == 2) { m = (T[0] < T[1]); SA[m ^ 1] = 0, SA[m] = 1; return 0; } + + /* Suffixsort. */ + if((ctx->bucket_A != NULL) && (ctx->bucket_B != NULL)) { + m = sort_typeBstar(T, SA, ctx->bucket_A, ctx->bucket_B, n); + construct_SA(T, SA, ctx->bucket_A, ctx->bucket_B, n, m); + } else { + err = -2; + } + + return err; +} + +#if 0 +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n) { + saidx_t *B; + saidx_t *bucket_A, *bucket_B; + saidx_t m, pidx, i; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0)) { return -1; } + else if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; } + + if((B = A) == NULL) { B = (saidx_t *)malloc((size_t)(n + 1) * sizeof(saidx_t)); } + bucket_A = (saidx_t *)malloc(BUCKET_A_SIZE * sizeof(saidx_t)); + bucket_B = (saidx_t *)malloc(BUCKET_B_SIZE * sizeof(saidx_t)); + + /* Burrows-Wheeler Transform. */ + if((B != NULL) && (bucket_A != NULL) && (bucket_B != NULL)) { + m = sort_typeBstar(T, B, bucket_A, bucket_B, n); + pidx = construct_BWT(T, B, bucket_A, bucket_B, n, m); + + /* Copy to output string. */ + U[0] = T[n - 1]; + for(i = 0; i < pidx; ++i) { U[i + 1] = (sauchar_t)B[i]; } + for(i += 1; i < n; ++i) { U[i] = (sauchar_t)B[i]; } + pidx += 1; + } else { + pidx = -2; + } + + free(bucket_B); + free(bucket_A); + if(A == NULL) { free(B); } + + return pidx; +} + +const char * +divsufsort_version(void) { + return PROJECT_VERSION_FULL; +} +#endif diff --git a/gbdk-support/gbcompress/zx0/libdivsufsort/divsufsort.h b/gbdk-support/gbcompress/zx0/libdivsufsort/divsufsort.h @@ -0,0 +1,189 @@ +/* + * divsufsort.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT_H +#define _DIVSUFSORT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define DIVSUFSORT_API + +/*- Datatypes -*/ +#ifndef SAUCHAR_T +#define SAUCHAR_T +typedef unsigned char sauchar_t; +#endif /* SAUCHAR_T */ +#ifndef SAINT_T +#define SAINT_T +typedef int saint_t; +#endif /* SAINT_T */ +#ifndef SAIDX_T +#define SAIDX_T +typedef int saidx_t; +#endif /* SAIDX_T */ +#ifndef PRIdSAIDX_T +#define PRIdSAIDX_T "d" +#endif + +/*- divsufsort context */ +typedef struct _divsufsort_ctx_t { + saidx_t *bucket_A; + saidx_t *bucket_B; +} divsufsort_ctx_t; + +/*- Prototypes -*/ + +/** + * Initialize suffix array context + * + * @return 0 for success, or non-zero in case of an error + */ +int divsufsort_init(divsufsort_ctx_t *ctx); + +/** + * Destroy suffix array context + * + * @param ctx suffix array context to destroy + */ +void divsufsort_destroy(divsufsort_ctx_t *ctx); + +/** + * Constructs the suffix array of a given string. + * @param ctx suffix array context + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The output array of suffixes. + * @param n The length of the given string. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t divsufsort_build_array(divsufsort_ctx_t *ctx, const sauchar_t *T, saidx_t *SA, saidx_t n); + +#if 0 +/** + * Constructs the burrows-wheeler transformed string of a given string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @return The primary index if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saidx_t +divbwt(const sauchar_t *T, sauchar_t *U, saidx_t *A, saidx_t n); + +/** + * Returns the version of the divsufsort library. + * @return The version number string. + */ +DIVSUFSORT_API +const char * +divsufsort_version(void); + + +/** + * Constructs the burrows-wheeler transformed string of a given string and suffix array. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param SA[0..n-1] The suffix array. (can be NULL) + * @param n The length of the given string. + * @param idx The output primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +bw_transform(const sauchar_t *T, sauchar_t *U, + saidx_t *SA /* can NULL */, + saidx_t n, saidx_t *idx); + +/** + * Inverse BW-transforms a given BWTed string. + * @param T[0..n-1] The input string. + * @param U[0..n-1] The output string. (can be T) + * @param A[0..n-1] The temporary array. (can be NULL) + * @param n The length of the given string. + * @param idx The primary index. + * @return 0 if no error occurred, -1 or -2 otherwise. + */ +DIVSUFSORT_API +saint_t +inverse_bw_transform(const sauchar_t *T, sauchar_t *U, + saidx_t *A /* can NULL */, + saidx_t n, saidx_t idx); + +/** + * Checks the correctness of a given suffix array. + * @param T[0..n-1] The input string. + * @param SA[0..n-1] The input suffix array. + * @param n The length of the given string. + * @param verbose The verbose mode. + * @return 0 if no error occurred. + */ +DIVSUFSORT_API +saint_t +sufcheck(const sauchar_t *T, const saidx_t *SA, saidx_t n, saint_t verbose); + +/** + * Search for the pattern P in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param P[0..Psize-1] The input pattern string. + * @param Psize The length of the given pattern string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx_t +sa_search(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + const saidx_t *SA, saidx_t SAsize, + saidx_t *left); + +/** + * Search for the character c in the string T. + * @param T[0..Tsize-1] The input string. + * @param Tsize The length of the given string. + * @param SA[0..SAsize-1] The input suffix array. + * @param SAsize The length of the given suffix array. + * @param c The input character. + * @param idx The output index. + * @return The count of matches if no error occurred, -1 otherwise. + */ +DIVSUFSORT_API +saidx_t +sa_simplesearch(const sauchar_t *T, saidx_t Tsize, + const saidx_t *SA, saidx_t SAsize, + saint_t c, saidx_t *left); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT_H */ diff --git a/gbdk-support/gbcompress/zx0/libdivsufsort/divsufsort_config.h b/gbdk-support/gbcompress/zx0/libdivsufsort/divsufsort_config.h @@ -0,0 +1,9 @@ +#define HAVE_STRING_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STDINT_H 1 +#define INLINE inline + +#ifdef _MSC_VER +#pragma warning( disable : 4244 ) +#endif /* _MSC_VER */ diff --git a/gbdk-support/gbcompress/zx0/libdivsufsort/divsufsort_private.h b/gbdk-support/gbcompress/zx0/libdivsufsort/divsufsort_private.h @@ -0,0 +1,205 @@ +/* + * divsufsort_private.h for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DIVSUFSORT_PRIVATE_H +#define _DIVSUFSORT_PRIVATE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "divsufsort_config.h" +#include <assert.h> +#include <stdio.h> +#if HAVE_STRING_H +# include <string.h> +#endif +#if HAVE_STDLIB_H +# include <stdlib.h> +#endif +#if HAVE_MEMORY_H +# include <memory.h> +#endif +#if HAVE_STDDEF_H +# include <stddef.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if defined(BUILD_DIVSUFSORT64) +# include "divsufsort64.h" +# ifndef SAIDX_T +# define SAIDX_T +# define saidx_t saidx64_t +# endif /* SAIDX_T */ +# ifndef PRIdSAIDX_T +# define PRIdSAIDX_T PRIdSAIDX64_T +# endif /* PRIdSAIDX_T */ +# define divsufsort divsufsort64 +# define divbwt divbwt64 +# define divsufsort_version divsufsort64_version +# define bw_transform bw_transform64 +# define inverse_bw_transform inverse_bw_transform64 +# define sufcheck sufcheck64 +# define sa_search sa_search64 +# define sa_simplesearch sa_simplesearch64 +# define sssort sssort64 +# define trsort trsort64 +#else +# include "divsufsort.h" +#endif + + +/*- Constants -*/ +#if !defined(UINT8_MAX) +# define UINT8_MAX (255) +#endif /* UINT8_MAX */ +#if defined(ALPHABET_SIZE) && (ALPHABET_SIZE < 1) +# undef ALPHABET_SIZE +#endif +#if !defined(ALPHABET_SIZE) +# define ALPHABET_SIZE (UINT8_MAX + 1) +#endif +/* for divsufsort.c */ +#define BUCKET_A_SIZE (ALPHABET_SIZE) +#define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE) +/* for sssort.c */ +#if defined(SS_INSERTIONSORT_THRESHOLD) +# if SS_INSERTIONSORT_THRESHOLD < 1 +# undef SS_INSERTIONSORT_THRESHOLD +# define SS_INSERTIONSORT_THRESHOLD (1) +# endif +#else +# define SS_INSERTIONSORT_THRESHOLD (8) +#endif +#if defined(SS_BLOCKSIZE) +# if SS_BLOCKSIZE < 0 +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (0) +# elif 32768 <= SS_BLOCKSIZE +# undef SS_BLOCKSIZE +# define SS_BLOCKSIZE (32767) +# endif +#else +# define SS_BLOCKSIZE (1024) +#endif +/* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */ +#if SS_BLOCKSIZE == 0 +# if defined(BUILD_DIVSUFSORT64) +# define SS_MISORT_STACKSIZE (96) +# else +# define SS_MISORT_STACKSIZE (64) +# endif +#elif SS_BLOCKSIZE <= 4096 +# define SS_MISORT_STACKSIZE (16) +#else +# define SS_MISORT_STACKSIZE (24) +#endif +#if defined(BUILD_DIVSUFSORT64) +# define SS_SMERGE_STACKSIZE (64) +#else +# define SS_SMERGE_STACKSIZE (32) +#endif +/* for trsort.c */ +#define TR_INSERTIONSORT_THRESHOLD (8) +#if defined(BUILD_DIVSUFSORT64) +# define TR_STACKSIZE (96) +#else +# define TR_STACKSIZE (64) +#endif + + +/*- Macros -*/ +#ifndef SWAP +# define SWAP(_a, _b) do { t = (_a); (_a) = (_b); (_b) = t; } while(0) +#endif /* SWAP */ +#ifndef MIN +# define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) +#endif /* MIN */ +#ifndef MAX +# define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) +#endif /* MAX */ +#define STACK_PUSH(_a, _b, _c, _d)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize++].d = (_d);\ + } while(0) +#define STACK_PUSH5(_a, _b, _c, _d, _e)\ + do {\ + assert(ssize < STACK_SIZE);\ + stack[ssize].a = (_a), stack[ssize].b = (_b),\ + stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\ + } while(0) +#define STACK_POP(_a, _b, _c, _d)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d;\ + } while(0) +#define STACK_POP5(_a, _b, _c, _d, _e)\ + do {\ + assert(0 <= ssize);\ + if(ssize == 0) { return; }\ + (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ + (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\ + } while(0) +/* for divsufsort.c */ +#define BUCKET_A(_c0) bucket_A[(_c0)] +#if ALPHABET_SIZE == 256 +#define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)]) +#else +#define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)]) +#define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)]) +#endif + + +/*- Private Prototypes -*/ +/* sssort.c */ +void +sssort(const sauchar_t *Td, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t *buf, saidx_t bufsize, + saidx_t depth, saidx_t n, saint_t lastsuffix); +/* trsort.c */ +void +trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* _DIVSUFSORT_PRIVATE_H */ diff --git a/gbdk-support/gbcompress/zx0/libdivsufsort/divsufsort_utils.c b/gbdk-support/gbcompress/zx0/libdivsufsort/divsufsort_utils.c @@ -0,0 +1,383 @@ +/* + * utils.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Function -*/ + +#if 0 +/* Binary search for inverse bwt. */ +static +saidx_t +binarysearch_lower(const saidx_t *A, saidx_t size, saidx_t value) { + saidx_t half, i; + for(i = 0, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + if(A[i + half] < value) { + i += half + 1; + half -= (size & 1) ^ 1; + } + } + return i; +} + + +/*- Functions -*/ + +/* Burrows-Wheeler transform. */ +saint_t +bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *SA, + saidx_t n, saidx_t *idx) { + saidx_t *A, i, j, p, t; + saint_t c; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0) || (idx == NULL)) { return -1; } + if(n <= 1) { + if(n == 1) { U[0] = T[0]; } + *idx = n; + return 0; + } + + if((A = SA) == NULL) { + i = divbwt(T, U, NULL, n); + if(0 <= i) { *idx = i; i = 0; } + return (saint_t)i; + } + + /* BW transform. */ + if(T == U) { + t = n; + for(i = 0, j = 0; i < n; ++i) { + p = t - 1; + t = A[i]; + if(0 <= p) { + c = T[j]; + U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; + A[j] = c; + j++; + } else { + *idx = i; + } + } + p = t - 1; + if(0 <= p) { + c = T[j]; + U[j] = (j <= p) ? T[p] : (sauchar_t)A[p]; + A[j] = c; + } else { + *idx = i; + } + } else { + U[0] = T[n - 1]; + for(i = 0; A[i] != 0; ++i) { U[i + 1] = T[A[i] - 1]; } + *idx = i + 1; + for(++i; i < n; ++i) { U[i] = T[A[i] - 1]; } + } + + if(SA == NULL) { + /* Deallocate memory. */ + free(A); + } + + return 0; +} + +/* Inverse Burrows-Wheeler transform. */ +saint_t +inverse_bw_transform(const sauchar_t *T, sauchar_t *U, saidx_t *A, + saidx_t n, saidx_t idx) { + saidx_t C[ALPHABET_SIZE]; + sauchar_t D[ALPHABET_SIZE]; + saidx_t *B; + saidx_t i, p; + saint_t c, d; + + /* Check arguments. */ + if((T == NULL) || (U == NULL) || (n < 0) || (idx < 0) || + (n < idx) || ((0 < n) && (idx == 0))) { + return -1; + } + if(n <= 1) { return 0; } + + if((B = A) == NULL) { + /* Allocate n*sizeof(saidx_t) bytes of memory. */ + if((B = (saidx_t *)malloc((size_t)n * sizeof(saidx_t))) == NULL) { return -2; } + } + + /* Inverse BW transform. */ + for(c = 0; c < ALPHABET_SIZE; ++c) { C[c] = 0; } + for(i = 0; i < n; ++i) { ++C[T[i]]; } + for(c = 0, d = 0, i = 0; c < ALPHABET_SIZE; ++c) { + p = C[c]; + if(0 < p) { + C[c] = i; + D[d++] = (sauchar_t)c; + i += p; + } + } + for(i = 0; i < idx; ++i) { B[C[T[i]]++] = i; } + for( ; i < n; ++i) { B[C[T[i]]++] = i + 1; } + for(c = 0; c < d; ++c) { C[c] = C[D[c]]; } + for(i = 0, p = idx; i < n; ++i) { + U[i] = D[binarysearch_lower(C, d, p)]; + p = B[p - 1]; + } + + if(A == NULL) { + /* Deallocate memory. */ + free(B); + } + + return 0; +} + +/* Checks the suffix array SA of the string T. */ +saint_t +sufcheck(const sauchar_t *T, const saidx_t *SA, + saidx_t n, saint_t verbose) { + saidx_t C[ALPHABET_SIZE]; + saidx_t i, p, q, t; + saint_t c; + + if(verbose) { fprintf(stderr, "sufcheck: "); } + + /* Check arguments. */ + if((T == NULL) || (SA == NULL) || (n < 0)) { + if(verbose) { fprintf(stderr, "Invalid arguments.\n"); } + return -1; + } + if(n == 0) { + if(verbose) { fprintf(stderr, "Done.\n"); } + return 0; + } + + /* check range: [0..n-1] */ + for(i = 0; i < n; ++i) { + if((SA[i] < 0) || (n <= SA[i])) { + if(verbose) { + fprintf(stderr, "Out of the range [0,%" PRIdSAIDX_T "].\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", + n - 1, i, SA[i]); + } + return -2; + } + } + + /* check first characters. */ + for(i = 1; i < n; ++i) { + if(T[SA[i - 1]] > T[SA[i]]) { + if(verbose) { + fprintf(stderr, "Suffixes in wrong order.\n" + " T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d" + " > T[SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "]=%d\n", + i - 1, SA[i - 1], T[SA[i - 1]], i, SA[i], T[SA[i]]); + } + return -3; + } + } + + /* check suffixes. */ + for(i = 0; i < ALPHABET_SIZE; ++i) { C[i] = 0; } + for(i = 0; i < n; ++i) { ++C[T[i]]; } + for(i = 0, p = 0; i < ALPHABET_SIZE; ++i) { + t = C[i]; + C[i] = p; + p += t; + } + + q = C[T[n - 1]]; + C[T[n - 1]] += 1; + for(i = 0; i < n; ++i) { + p = SA[i]; + if(0 < p) { + c = T[--p]; + t = C[c]; + } else { + c = T[p = n - 1]; + t = q; + } + if((t < 0) || (p != SA[t])) { + if(verbose) { + fprintf(stderr, "Suffix in wrong position.\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T " or\n" + " SA[%" PRIdSAIDX_T "]=%" PRIdSAIDX_T "\n", + t, (0 <= t) ? SA[t] : -1, i, SA[i]); + } + return -4; + } + if(t != q) { + ++C[c]; + if((n <= C[c]) || (T[SA[C[c]]] != c)) { C[c] = -1; } + } + } + + if(1 <= verbose) { fprintf(stderr, "Done.\n"); } + return 0; +} + + +static +int +_compare(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + saidx_t suf, saidx_t *match) { + saidx_t i, j; + saint_t r; + for(i = suf + *match, j = *match, r = 0; + (i < Tsize) && (j < Psize) && ((r = T[i] - P[j]) == 0); ++i, ++j) { } + *match = j; + return (r == 0) ? -(j != Psize) : r; +} + +/* Search for the pattern P in the string T. */ +saidx_t +sa_search(const sauchar_t *T, saidx_t Tsize, + const sauchar_t *P, saidx_t Psize, + const saidx_t *SA, saidx_t SAsize, + saidx_t *idx) { + saidx_t size, lsize, rsize, half; + saidx_t match, lmatch, rmatch; + saidx_t llmatch, lrmatch, rlmatch, rrmatch; + saidx_t i, j, k; + saint_t r; + + if(idx != NULL) { *idx = -1; } + if((T == NULL) || (P == NULL) || (SA == NULL) || + (Tsize < 0) || (Psize < 0) || (SAsize < 0)) { return -1; } + if((Tsize == 0) || (SAsize == 0)) { return 0; } + if(Psize == 0) { if(idx != NULL) { *idx = 0; } return SAsize; } + + for(i = j = k = 0, lmatch = rmatch = 0, size = SAsize, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + match = MIN(lmatch, rmatch); + r = _compare(T, Tsize, P, Psize, SA[i + half], &match); + if(r < 0) { + i += half + 1; + half -= (size & 1) ^ 1; + lmatch = match; + } else if(r > 0) { + rmatch = match; + } else { + lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; + + /* left part */ + for(llmatch = lmatch, lrmatch = match, half = lsize >> 1; + 0 < lsize; + lsize = half, half >>= 1) { + lmatch = MIN(llmatch, lrmatch); + r = _compare(T, Tsize, P, Psize, SA[j + half], &lmatch); + if(r < 0) { + j += half + 1; + half -= (lsize & 1) ^ 1; + llmatch = lmatch; + } else { + lrmatch = lmatch; + } + } + + /* right part */ + for(rlmatch = match, rrmatch = rmatch, half = rsize >> 1; + 0 < rsize; + rsize = half, half >>= 1) { + rmatch = MIN(rlmatch, rrmatch); + r = _compare(T, Tsize, P, Psize, SA[k + half], &rmatch); + if(r <= 0) { + k += half + 1; + half -= (rsize & 1) ^ 1; + rlmatch = rmatch; + } else { + rrmatch = rmatch; + } + } + + break; + } + } + + if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } + return k - j; +} + +/* Search for the character c in the string T. */ +saidx_t +sa_simplesearch(const sauchar_t *T, saidx_t Tsize, + const saidx_t *SA, saidx_t SAsize, + saint_t c, saidx_t *idx) { + saidx_t size, lsize, rsize, half; + saidx_t i, j, k, p; + saint_t r; + + if(idx != NULL) { *idx = -1; } + if((T == NULL) || (SA == NULL) || (Tsize < 0) || (SAsize < 0)) { return -1; } + if((Tsize == 0) || (SAsize == 0)) { return 0; } + + for(i = j = k = 0, size = SAsize, half = size >> 1; + 0 < size; + size = half, half >>= 1) { + p = SA[i + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r < 0) { + i += half + 1; + half -= (size & 1) ^ 1; + } else if(r == 0) { + lsize = half, j = i, rsize = size - half - 1, k = i + half + 1; + + /* left part */ + for(half = lsize >> 1; + 0 < lsize; + lsize = half, half >>= 1) { + p = SA[j + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r < 0) { + j += half + 1; + half -= (lsize & 1) ^ 1; + } + } + + /* right part */ + for(half = rsize >> 1; + 0 < rsize; + rsize = half, half >>= 1) { + p = SA[k + half]; + r = (p < Tsize) ? T[p] - c : -1; + if(r <= 0) { + k += half + 1; + half -= (rsize & 1) ^ 1; + } + } + + break; + } + } + + if(idx != NULL) { *idx = (0 < (k - j)) ? j : i; } + return k - j; +} +#endif diff --git a/gbdk-support/gbcompress/zx0/libdivsufsort/sssort.c b/gbdk-support/gbcompress/zx0/libdivsufsort/sssort.c @@ -0,0 +1,815 @@ +/* + * sssort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Functions -*/ + +static const saint_t lg_table[256]= { + -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +saint_t +ss_ilg(saidx_t n) { +#if SS_BLOCKSIZE == 0 +# if defined(BUILD_DIVSUFSORT64) + return (n >> 32) ? + ((n >> 48) ? + ((n >> 56) ? + 56 + lg_table[(n >> 56) & 0xff] : + 48 + lg_table[(n >> 48) & 0xff]) : + ((n >> 40) ? + 40 + lg_table[(n >> 40) & 0xff] : + 32 + lg_table[(n >> 32) & 0xff])) : + ((n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff])); +# else + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +# endif +#elif SS_BLOCKSIZE < 256 + return lg_table[n]; +#else + return (n & 0xff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]; +#endif +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + +#if SS_BLOCKSIZE != 0 + +static const saint_t sqq_table[256] = { + 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, + 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, + 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, +110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, +128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, +143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, +156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, +169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, +181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, +192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, +202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, +212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, +221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, +230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, +239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, +247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255 +}; + +static INLINE +saidx_t +ss_isqrt(saidx_t x) { + saidx_t y, e; + + if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; } + e = (x & 0xffff0000) ? + ((x & 0xff000000) ? + 24 + lg_table[(x >> 24) & 0xff] : + 16 + lg_table[(x >> 16) & 0xff]) : + ((x & 0x0000ff00) ? + 8 + lg_table[(x >> 8) & 0xff] : + 0 + lg_table[(x >> 0) & 0xff]); + + if(e >= 16) { + y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7); + if(e >= 24) { y = (y + 1 + x / y) >> 1; } + y = (y + 1 + x / y) >> 1; + } else if(e >= 8) { + y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1; + } else { + return sqq_table[x] >> 4; + } + + return (x < (y * y)) ? y - 1 : y; +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/* Compares two suffixes. */ +static INLINE +saint_t +ss_compare(const sauchar_t *T, + const saidx_t *p1, const saidx_t *p2, + saidx_t depth) { + const sauchar_t *U1, *U2, *U1n, *U2n; + + for(U1 = T + depth + *p1, + U2 = T + depth + *p2, + U1n = T + *(p1 + 1) + 2, + U2n = T + *(p2 + 1) + 2; + (U1 < U1n) && (U2 < U2n) && (*U1 == *U2); + ++U1, ++U2) { + } + + return U1 < U1n ? + (U2 < U2n ? *U1 - *U2 : 1) : + (U2 < U2n ? -1 : 0); +} + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) + +/* Insertionsort for small size groups */ +static +void +ss_insertionsort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, saidx_t depth) { + saidx_t *i, *j; + saidx_t t; + saint_t r; + + for(i = last - 2; first <= i; --i) { + for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) { + do { *(j - 1) = *j; } while((++j < last) && (*j < 0)); + if(last <= j) { break; } + } + if(r == 0) { *j = ~*j; } + *(j - 1) = t; + } +} + +#endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */ + + +/*---------------------------------------------------------------------------*/ + +#if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) + +static INLINE +void +ss_fixdown(const sauchar_t *Td, const saidx_t *PA, + saidx_t *SA, saidx_t i, saidx_t size) { + saidx_t j, k; + saidx_t v; + saint_t c, d, e; + + for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = Td[PA[SA[k = j++]]]; + if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +ss_heapsort(const sauchar_t *Td, const saidx_t *PA, saidx_t *SA, saidx_t size) { + saidx_t i, m; + saidx_t t; + + m = size; + if((size % 2) == 0) { + m--; + if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + ss_fixdown(Td, PA, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +saidx_t * +ss_median3(const sauchar_t *Td, const saidx_t *PA, + saidx_t *v1, saidx_t *v2, saidx_t *v3) { + saidx_t *t; + if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); } + if(Td[PA[*v2]] > Td[PA[*v3]]) { + if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +saidx_t * +ss_median5(const sauchar_t *Td, const saidx_t *PA, + saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { + saidx_t *t; + if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); } + if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); } + if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); } + if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); } + if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); } + if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +saidx_t * +ss_pivot(const sauchar_t *Td, const saidx_t *PA, saidx_t *first, saidx_t *last) { + saidx_t *middle; + saidx_t t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return ss_median3(Td, PA, first, middle, last - 1); + } else { + t >>= 2; + return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = ss_median3(Td, PA, first, first + t, first + (t << 1)); + middle = ss_median3(Td, PA, middle - t, middle, middle + t); + last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1); + return ss_median3(Td, PA, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +/* Binary partition for substrings. */ +static INLINE +saidx_t * +ss_partition(const saidx_t *PA, + saidx_t *first, saidx_t *last, saidx_t depth) { + saidx_t *a, *b; + saidx_t t; + for(a = first - 1, b = last;;) { + for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; } + for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));) { } + if(b <= a) { break; } + t = ~*b; + *b = *a; + *a = t; + } + if(first < a) { *first = ~*first; } + return a; +} + +/* Multikey introsort for medium size groups. */ +static +void +ss_mintrosort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t depth) { +#define STACK_SIZE SS_MISORT_STACKSIZE + struct { saidx_t *a, *b, c; saint_t d; } stack[STACK_SIZE]; + const sauchar_t *Td; + saidx_t *a, *b, *c, *d, *e, *f; + saidx_t s, t; + saint_t ssize; + saint_t limit; + saint_t v, x = 0; + + for(ssize = 0, limit = ss_ilg(last - first);;) { + + if((last - first) <= SS_INSERTIONSORT_THRESHOLD) { +#if 1 < SS_INSERTIONSORT_THRESHOLD + if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); } +#endif + STACK_POP(first, last, depth, limit); + continue; + } + + Td = T + depth; + if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); } + if(limit < 0) { + for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) { + if((x = Td[PA[*a]]) != v) { + if(1 < (a - first)) { break; } + v = x; + first = a; + } + } + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, a, depth); + } + if((a - first) <= (last - a)) { + if(1 < (a - first)) { + STACK_PUSH(a, last, depth, -1); + last = a, depth += 1, limit = ss_ilg(a - first); + } else { + first = a, limit = -1; + } + } else { + if(1 < (last - a)) { + STACK_PUSH(first, a, depth + 1, ss_ilg(a - first)); + first = a, limit = -1; + } else { + last = a, depth += 1, limit = ss_ilg(a - first); + } + } + continue; + } + + /* choose pivot */ + a = ss_pivot(Td, PA, first, last); + v = Td[PA[*a]]; + SWAP(*first, *a); + + /* partition */ + for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + + a = first + (b - a), c = last - (d - c); + b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth); + + if((a - first) <= (last - c)) { + if((last - c) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(c, last, depth, limit); + last = a; + } else if((a - first) <= (c - b)) { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + last = a; + } else { + STACK_PUSH(c, last, depth, limit); + STACK_PUSH(first, a, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } else { + if((a - first) <= (c - b)) { + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + STACK_PUSH(first, a, depth, limit); + first = c; + } else if((last - c) <= (c - b)) { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); + first = c; + } else { + STACK_PUSH(first, a, depth, limit); + STACK_PUSH(c, last, depth, limit); + first = b, last = c, depth += 1, limit = ss_ilg(c - b); + } + } + } else { + limit += 1; + if(Td[PA[*first] - 1] < v) { + first = ss_partition(PA, first, last, depth); + limit = ss_ilg(last - first); + } + depth += 1; + } + } +#undef STACK_SIZE +} + +#endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ + + +/*---------------------------------------------------------------------------*/ + +#if SS_BLOCKSIZE != 0 + +static INLINE +void +ss_blockswap(saidx_t *a, saidx_t *b, saidx_t n) { + saidx_t t; + for(; 0 < n; --n, ++a, ++b) { + t = *a, *a = *b, *b = t; + } +} + +static INLINE +void +ss_rotate(saidx_t *first, saidx_t *middle, saidx_t *last) { + saidx_t *a, *b, t; + saidx_t l, r; + l = middle - first, r = last - middle; + for(; (0 < l) && (0 < r);) { + if(l == r) { ss_blockswap(first, middle, l); break; } + if(l < r) { + a = last - 1, b = middle - 1; + t = *a; + do { + *a-- = *b, *b-- = *a; + if(b < first) { + *a = t; + last = a; + if((r -= l + 1) <= l) { break; } + a -= 1, b = middle - 1; + t = *a; + } + } while(1); + } else { + a = first, b = middle; + t = *a; + do { + *a++ = *b, *b++ = *a; + if(last <= b) { + *a = t; + first = a + 1; + if((l -= r + 1) <= r) { break; } + a += 1, b = middle; + t = *a; + } + } while(1); + } + } +} + + +/*---------------------------------------------------------------------------*/ + +static +void +ss_inplacemerge(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t depth) { + const saidx_t *p; + saidx_t *a, *b; + saidx_t len, half; + saint_t q, r; + saint_t x; + + for(;;) { + if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); } + else { x = 0; p = PA + *(last - 1); } + for(a = first, len = middle - first, half = len >> 1, r = -1; + 0 < len; + len = half, half >>= 1) { + b = a + half; + q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth); + if(q < 0) { + a = b + 1; + half -= (len & 1) ^ 1; + } else { + r = q; + } + } + if(a < middle) { + if(r == 0) { *a = ~*a; } + ss_rotate(a, middle, last); + last -= middle - a; + middle = a; + if(first == middle) { break; } + } + --last; + if(x != 0) { while(*--last < 0) { } } + if(middle == last) { break; } + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Merge-forward with internal buffer. */ +static +void +ss_mergeforward(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t depth) { + saidx_t *a, *b, *c, *bufend; + saidx_t t; + saint_t r; + + bufend = buf + (middle - first) - 1; + ss_blockswap(buf, first, middle - first); + + for(t = *(a = first), b = buf, c = middle;;) { + r = ss_compare(T, PA + *b, PA + *c, depth); + if(r < 0) { + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + } else if(r > 0) { + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } else { + *c = ~*c; + do { + *a++ = *b; + if(bufend <= b) { *bufend = t; return; } + *b++ = *a; + } while(*b < 0); + + do { + *a++ = *c, *c++ = *a; + if(last <= c) { + while(b < bufend) { *a++ = *b, *b++ = *a; } + *a = *b, *b = t; + return; + } + } while(*c < 0); + } + } +} + +/* Merge-backward with internal buffer. */ +static +void +ss_mergebackward(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t depth) { + const saidx_t *p1, *p2; + saidx_t *a, *b, *c, *bufend; + saidx_t t; + saint_t r; + saint_t x; + + bufend = buf + (last - middle) - 1; + ss_blockswap(buf, middle, last - middle); + + x = 0; + if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; } + else { p1 = PA + *bufend; } + if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; } + else { p2 = PA + *(middle - 1); } + for(t = *(a = last - 1), b = bufend, c = middle - 1;;) { + r = ss_compare(T, p1, p2, depth); + if(0 < r) { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = *b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + } else if(r < 0) { + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } else { + if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } + *a-- = ~*b; + if(b <= buf) { *buf = t; break; } + *b-- = *a; + if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } + *a-- = *c, *c-- = *a; + if(c < first) { + while(buf < b) { *a-- = *b, *b-- = *a; } + *a = *b, *b = t; + break; + } + if(*b < 0) { p1 = PA + ~*b; x |= 1; } + else { p1 = PA + *b; } + if(*c < 0) { p2 = PA + ~*c; x |= 2; } + else { p2 = PA + *c; } + } + } +} + +/* D&C based merge. */ +static +void +ss_swapmerge(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t *buf, saidx_t bufsize, saidx_t depth) { +#define STACK_SIZE SS_SMERGE_STACKSIZE +#define GETIDX(a) ((0 <= (a)) ? (a) : (~(a))) +#define MERGE_CHECK(a, b, c)\ + do {\ + if(((c) & 1) ||\ + (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\ + *(a) = ~*(a);\ + }\ + if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\ + *(b) = ~*(b);\ + }\ + } while(0) + struct { saidx_t *a, *b, *c; saint_t d; } stack[STACK_SIZE]; + saidx_t *l, *r, *lm, *rm; + saidx_t m, len, half; + saint_t ssize; + saint_t check, next; + + for(check = 0, ssize = 0;;) { + if((last - middle) <= bufsize) { + if((first < middle) && (middle < last)) { + ss_mergebackward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + if((middle - first) <= bufsize) { + if(first < middle) { + ss_mergeforward(T, PA, first, middle, last, buf, depth); + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + continue; + } + + for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1; + 0 < len; + len = half, half >>= 1) { + if(ss_compare(T, PA + GETIDX(*(middle + m + half)), + PA + GETIDX(*(middle - m - half - 1)), depth) < 0) { + m += half + 1; + half -= (len & 1) ^ 1; + } + } + + if(0 < m) { + lm = middle - m, rm = middle + m; + ss_blockswap(lm, middle, m); + l = r = middle, next = 0; + if(rm < last) { + if(*rm < 0) { + *rm = ~*rm; + if(first < lm) { for(; *--l < 0;) { } next |= 4; } + next |= 1; + } else if(first < lm) { + for(; *r < 0; ++r) { } + next |= 2; + } + } + + if((l - first) <= (last - r)) { + STACK_PUSH(r, rm, last, (next & 3) | (check & 4)); + middle = lm, last = l, check = (check & 3) | (next & 4); + } else { + if((next & 2) && (r == middle)) { next ^= 6; } + STACK_PUSH(first, lm, l, (check & 3) | (next & 4)); + first = r, middle = rm, check = (next & 3) | (check & 4); + } + } else { + if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) { + *middle = ~*middle; + } + MERGE_CHECK(first, last, check); + STACK_POP(first, middle, last, check); + } + } +#undef STACK_SIZE +} + +#endif /* SS_BLOCKSIZE != 0 */ + + +/*---------------------------------------------------------------------------*/ + +/*- Function -*/ + +/* Substring sort */ +void +sssort(const sauchar_t *T, const saidx_t *PA, + saidx_t *first, saidx_t *last, + saidx_t *buf, saidx_t bufsize, + saidx_t depth, saidx_t n, saint_t lastsuffix) { + saidx_t *a; +#if SS_BLOCKSIZE != 0 + saidx_t *b, *middle, *curbuf; + saidx_t j, k, curbufsize, limit; +#endif + saidx_t i; + + if(lastsuffix != 0) { ++first; } + +#if SS_BLOCKSIZE == 0 + ss_mintrosort(T, PA, first, last, depth); +#else + if((bufsize < SS_BLOCKSIZE) && + (bufsize < (last - first)) && + (bufsize < (limit = ss_isqrt(last - first)))) { + if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; } + buf = middle = last - limit, bufsize = limit; + } else { + middle = last, limit = 0; + } + for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth); +#endif + curbufsize = last - (a + SS_BLOCKSIZE); + curbuf = a + SS_BLOCKSIZE; + if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; } + for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) { + ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth); + } + } +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, a, middle, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, a, middle, depth); +#endif + for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { + if(i & 1) { + ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth); + a -= k; + } + } + if(limit != 0) { +#if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE + ss_mintrosort(T, PA, middle, last, depth); +#elif 1 < SS_BLOCKSIZE + ss_insertionsort(T, PA, middle, last, depth); +#endif + ss_inplacemerge(T, PA, first, middle, last, depth); + } +#endif + + if(lastsuffix != 0) { + /* Insert last type B* suffix. */ + saidx_t PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2; + for(a = first, i = *(first - 1); + (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth))); + ++a) { + *(a - 1) = *a; + } + *(a - 1) = i; + } +} diff --git a/gbdk-support/gbcompress/zx0/libdivsufsort/trsort.c b/gbdk-support/gbcompress/zx0/libdivsufsort/trsort.c @@ -0,0 +1,586 @@ +/* + * trsort.c for libdivsufsort + * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "divsufsort_private.h" + + +/*- Private Functions -*/ + +static const saint_t lg_table[256]= { + -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; + +static INLINE +saint_t +tr_ilg(saidx_t n) { +#if defined(BUILD_DIVSUFSORT64) + return (n >> 32) ? + ((n >> 48) ? + ((n >> 56) ? + 56 + lg_table[(n >> 56) & 0xff] : + 48 + lg_table[(n >> 48) & 0xff]) : + ((n >> 40) ? + 40 + lg_table[(n >> 40) & 0xff] : + 32 + lg_table[(n >> 32) & 0xff])) : + ((n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff])); +#else + return (n & 0xffff0000) ? + ((n & 0xff000000) ? + 24 + lg_table[(n >> 24) & 0xff] : + 16 + lg_table[(n >> 16) & 0xff]) : + ((n & 0x0000ff00) ? + 8 + lg_table[(n >> 8) & 0xff] : + 0 + lg_table[(n >> 0) & 0xff]); +#endif +} + + +/*---------------------------------------------------------------------------*/ + +/* Simple insertionsort for small size groups. */ +static +void +tr_insertionsort(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { + saidx_t *a, *b; + saidx_t t, r; + + for(a = first + 1; a < last; ++a) { + for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) { + do { *(b + 1) = *b; } while((first <= --b) && (*b < 0)); + if(b < first) { break; } + } + if(r == 0) { *b = ~*b; } + *(b + 1) = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_fixdown(const saidx_t *ISAd, saidx_t *SA, saidx_t i, saidx_t size) { + saidx_t j, k; + saidx_t v; + saidx_t c, d, e; + + for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { + d = ISAd[SA[k = j++]]; + if(d < (e = ISAd[SA[j]])) { k = j; d = e; } + if(d <= c) { break; } + } + SA[i] = v; +} + +/* Simple top-down heapsort. */ +static +void +tr_heapsort(const saidx_t *ISAd, saidx_t *SA, saidx_t size) { + saidx_t i, m; + saidx_t t; + + m = size; + if((size % 2) == 0) { + m--; + if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); } + } + + for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); } + if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); } + for(i = m - 1; 0 < i; --i) { + t = SA[0], SA[0] = SA[i]; + tr_fixdown(ISAd, SA, 0, i); + SA[i] = t; + } +} + + +/*---------------------------------------------------------------------------*/ + +/* Returns the median of three elements. */ +static INLINE +saidx_t * +tr_median3(const saidx_t *ISAd, saidx_t *v1, saidx_t *v2, saidx_t *v3) { + saidx_t *t; + if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); } + if(ISAd[*v2] > ISAd[*v3]) { + if(ISAd[*v1] > ISAd[*v3]) { return v1; } + else { return v3; } + } + return v2; +} + +/* Returns the median of five elements. */ +static INLINE +saidx_t * +tr_median5(const saidx_t *ISAd, + saidx_t *v1, saidx_t *v2, saidx_t *v3, saidx_t *v4, saidx_t *v5) { + saidx_t *t; + if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); } + if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); } + if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); } + if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); } + if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); } + if(ISAd[*v3] > ISAd[*v4]) { return v4; } + return v3; +} + +/* Returns the pivot element. */ +static INLINE +saidx_t * +tr_pivot(const saidx_t *ISAd, saidx_t *first, saidx_t *last) { + saidx_t *middle; + saidx_t t; + + t = last - first; + middle = first + t / 2; + + if(t <= 512) { + if(t <= 32) { + return tr_median3(ISAd, first, middle, last - 1); + } else { + t >>= 2; + return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1); + } + } + t >>= 3; + first = tr_median3(ISAd, first, first + t, first + (t << 1)); + middle = tr_median3(ISAd, middle - t, middle, middle + t); + last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1); + return tr_median3(ISAd, first, middle, last); +} + + +/*---------------------------------------------------------------------------*/ + +typedef struct _trbudget_t trbudget_t; +struct _trbudget_t { + saidx_t chance; + saidx_t remain; + saidx_t incval; + saidx_t count; +}; + +static INLINE +void +trbudget_init(trbudget_t *budget, saidx_t chance, saidx_t incval) { + budget->chance = chance; + budget->remain = budget->incval = incval; +} + +static INLINE +saint_t +trbudget_check(trbudget_t *budget, saidx_t size) { + if(size <= budget->remain) { budget->remain -= size; return 1; } + if(budget->chance == 0) { budget->count += size; return 0; } + budget->remain += budget->incval - size; + budget->chance -= 1; + return 1; +} + + +/*---------------------------------------------------------------------------*/ + +static INLINE +void +tr_partition(const saidx_t *ISAd, + saidx_t *first, saidx_t *middle, saidx_t *last, + saidx_t **pa, saidx_t **pb, saidx_t v) { + saidx_t *a, *b, *c, *d, *e, *f; + saidx_t t, s; + saidx_t x = 0; + + for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { } + if(((a = b) < last) && (x < v)) { + for(; (++b < last) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + } + for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { } + if((b < (d = c)) && (x > v)) { + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + for(; b < c;) { + SWAP(*b, *c); + for(; (++b < c) && ((x = ISAd[*b]) <= v);) { + if(x == v) { SWAP(*b, *a); ++a; } + } + for(; (b < --c) && ((x = ISAd[*c]) >= v);) { + if(x == v) { SWAP(*c, *d); --d; } + } + } + + if(a <= d) { + c = b - 1; + if((s = a - first) > (t = b - a)) { s = t; } + for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + if((s = d - c) > (t = last - d - 1)) { s = t; } + for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } + first += (b - a), last -= (d - c); + } + *pa = first, *pb = last; +} + +static +void +tr_copy(saidx_t *ISA, const saidx_t *SA, + saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, + saidx_t depth) { + /* sort suffixes of middle partition + by using sorted order of suffixes of left and right partition. */ + saidx_t *c, *d, *e; + saidx_t s, v; + + v = b - SA - 1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + ISA[s] = d - SA; + } + } + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + ISA[s] = d - SA; + } + } +} + +static +void +tr_partialcopy(saidx_t *ISA, const saidx_t *SA, + saidx_t *first, saidx_t *a, saidx_t *b, saidx_t *last, + saidx_t depth) { + saidx_t *c, *d, *e; + saidx_t s, v; + saidx_t rank, lastrank, newrank = -1; + + v = b - SA - 1; + lastrank = -1; + for(c = first, d = a - 1; c <= d; ++c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *++d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } + + lastrank = -1; + for(e = d; first <= e; --e) { + rank = ISA[*e]; + if(lastrank != rank) { lastrank = rank; newrank = e - SA; } + if(newrank != rank) { ISA[*e] = newrank; } + } + + lastrank = -1; + for(c = last - 1, e = d + 1, d = b; e < d; --c) { + if((0 <= (s = *c - depth)) && (ISA[s] == v)) { + *--d = s; + rank = ISA[s + depth]; + if(lastrank != rank) { lastrank = rank; newrank = d - SA; } + ISA[s] = newrank; + } + } +} + +static +void +tr_introsort(saidx_t *ISA, const saidx_t *ISAd, + saidx_t *SA, saidx_t *first, saidx_t *last, + trbudget_t *budget) { +#define STACK_SIZE TR_STACKSIZE + struct { const saidx_t *a; saidx_t *b, *c; saint_t d, e; }stack[STACK_SIZE]; + saidx_t *a, *b, *c; + saidx_t t; + saidx_t v, x = 0; + saidx_t incr = ISAd - ISA; + saint_t limit, next; + saint_t ssize, trlink = -1; + + for(ssize = 0, limit = tr_ilg(last - first);;) { + + if(limit < 0) { + if(limit == -1) { + /* tandem repeat partition */ + tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1); + + /* update ranks */ + if(a < last) { + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + } + if(b < last) { + for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } + } + + /* push */ + if(1 < (b - a)) { + STACK_PUSH5(NULL, a, b, 0, 0); + STACK_PUSH5(ISAd - incr, first, last, -2, trlink); + trlink = ssize - 2; + } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink); + last = a, limit = tr_ilg(a - first); + } else if(1 < (last - b)) { + first = b, limit = tr_ilg(last - b); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink); + first = b, limit = tr_ilg(last - b); + } else if(1 < (a - first)) { + last = a, limit = tr_ilg(a - first); + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else if(limit == -2) { + /* tandem repeat copy */ + a = stack[--ssize].b, b = stack[ssize].c; + if(stack[ssize].d == 0) { + tr_copy(ISA, SA, first, a, b, last, ISAd - ISA); + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA); + } + STACK_POP5(ISAd, first, last, limit, trlink); + } else { + /* sorted partition */ + if(0 <= *first) { + a = first; + do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a)); + first = a; + } + if(first < last) { + a = first; do { *a = ~*a; } while(*++a < 0); + next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1; + if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } } + + /* push */ + if(trbudget_check(budget, a - first)) { + if((a - first) <= (last - a)) { + STACK_PUSH5(ISAd, a, last, -3, trlink); + ISAd += incr, last = a, limit = next; + } else { + if(1 < (last - a)) { + STACK_PUSH5(ISAd + incr, first, a, next, trlink); + first = a, limit = -3; + } else { + ISAd += incr, last = a, limit = next; + } + } + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + if(1 < (last - a)) { + first = a, limit = -3; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + continue; + } + + if((last - first) <= TR_INSERTIONSORT_THRESHOLD) { + tr_insertionsort(ISAd, first, last); + limit = -3; + continue; + } + + if(limit-- == 0) { + tr_heapsort(ISAd, first, last - first); + for(a = last - 1; first < a; a = b) { + for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; } + } + limit = -3; + continue; + } + + /* choose pivot */ + a = tr_pivot(ISAd, first, last); + SWAP(*first, *a); + v = ISAd[*first]; + + /* partition */ + tr_partition(ISAd, first, first + 1, last, &a, &b, v); + if((last - first) != (b - a)) { + next = (ISA[*a] != v) ? tr_ilg(b - a) : -1; + + /* update ranks */ + for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } + if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } + + /* push */ + if((1 < (b - a)) && (trbudget_check(budget, b - a))) { + if((a - first) <= (last - b)) { + if((last - b) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((a - first) <= (b - a)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, b, last, limit, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + if((a - first) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + last = a; + } else { + ISAd += incr, first = a, last = b, limit = next; + } + } else if((last - b) <= (b - a)) { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd + incr, a, b, next, trlink); + first = b; + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } else { + STACK_PUSH5(ISAd, first, a, limit, trlink); + STACK_PUSH5(ISAd, b, last, limit, trlink); + ISAd += incr, first = a, last = b, limit = next; + } + } + } else { + if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; } + if((a - first) <= (last - b)) { + if(1 < (a - first)) { + STACK_PUSH5(ISAd, b, last, limit, trlink); + last = a; + } else if(1 < (last - b)) { + first = b; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } else { + if(1 < (last - b)) { + STACK_PUSH5(ISAd, first, a, limit, trlink); + first = b; + } else if(1 < (a - first)) { + last = a; + } else { + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } + } else { + if(trbudget_check(budget, last - first)) { + limit = tr_ilg(last - first), ISAd += incr; + } else { + if(0 <= trlink) { stack[trlink].d = -1; } + STACK_POP5(ISAd, first, last, limit, trlink); + } + } + } +#undef STACK_SIZE +} + + + +/*---------------------------------------------------------------------------*/ + +/*- Function -*/ + +/* Tandem repeat sort */ +void +trsort(saidx_t *ISA, saidx_t *SA, saidx_t n, saidx_t depth) { + saidx_t *ISAd; + saidx_t *first, *last; + trbudget_t budget; + saidx_t t, skip, unsorted; + + trbudget_init(&budget, tr_ilg(n) * 2 / 3, n); +/* trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */ + for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) { + first = SA; + skip = 0; + unsorted = 0; + do { + if((t = *first) < 0) { first -= t; skip += t; } + else { + if(skip != 0) { *(first + skip) = skip; skip = 0; } + last = SA + ISA[t] + 1; + if(1 < (last - first)) { + budget.count = 0; + tr_introsort(ISA, ISAd, SA, first, last, &budget); + if(budget.count != 0) { unsorted += budget.count; } + else { skip = first - last; } + } else if((last - first) == 1) { + skip = -1; + } + first = last; + } + } while(first < (SA + n)); + if(skip != 0) { *(first + skip) = skip; } + if(unsorted == 0) { break; } + } +} diff --git a/gbdk-support/gbcompress/zx0/zx0_expand.c b/gbdk-support/gbcompress/zx0/zx0_expand.c @@ -0,0 +1,329 @@ +/* + * expand.c - decompressor implementation + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#include <stdlib.h> +#include <string.h> +#include "zx0_format.h" +#include "zx0_expand.h" +#include "zx0_libsalvador.h" + +#ifdef _MSC_VER +#define FORCE_INLINE __forceinline +#else /* _MSC_VER */ +#define FORCE_INLINE __attribute__((always_inline)) +#endif /* _MSC_VER */ + +static inline FORCE_INLINE int salvador_read_bit(const unsigned char **ppInBlock, const unsigned char *pDataEnd, int *nCurBitMask, unsigned char *bits) { + int nBit; + + const unsigned char* pInBlock = *ppInBlock; + + if ((*nCurBitMask) == 0) { + if (pInBlock >= pDataEnd) return -1; + (*bits) = *pInBlock++; + (*nCurBitMask) = 128; + } + + nBit = ((*bits) & 128) ? 1 : 0; + + (*bits) <<= 1; + (*nCurBitMask) >>= 1; + + *ppInBlock = pInBlock; + return nBit; +} + +static inline FORCE_INLINE int salvador_read_elias(const unsigned char** ppInBlock, const unsigned char* pDataEnd, const int nInitialValue, const int nIsBackward, int* nCurBitMask, unsigned char* bits) { + int nValue = nInitialValue; + + if (nIsBackward) { + while (salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits) == 1) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + } + } + else { + while (!salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits)) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + } + } + + return nValue; +} + +static inline FORCE_INLINE int salvador_read_elias_inverted(const unsigned char** ppInBlock, const unsigned char* pDataEnd, const int nInitialValue, int* nCurBitMask, unsigned char* bits) { + int nValue = nInitialValue; + + while (!salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits)) { + nValue = (nValue << 1) | (salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits) ^ 1); + } + + return nValue; +} + +static inline FORCE_INLINE int salvador_read_elias_prefix(const unsigned char** ppInBlock, const unsigned char* pDataEnd, const int nInitialValue, const int nIsBackward, int* nCurBitMask, unsigned char* bits, unsigned int nFirstBit) { + int nValue = nInitialValue; + + if (nIsBackward) { + if (nFirstBit) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + while (salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits) == 1) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + } + } + } + else { + if (!nFirstBit) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + while (!salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits)) { + nValue = (nValue << 1) | salvador_read_bit(ppInBlock, pDataEnd, nCurBitMask, bits); + } + } + } + + return nValue; +} + +/** + * Get maximum decompressed size of compressed data + * + * @param pInputData compressed data + * @param nInputSize compressed size in bytes + * @param nFlags compression flags (set to FLG_IS_INVERTED) + * + * @return maximum decompressed size + */ +size_t salvador_get_max_decompressed_size(const unsigned char *pInputData, size_t nInputSize, const unsigned int nFlags) { + const unsigned char* pInputDataEnd = pInputData + nInputSize; + int nCurBitMask = 0; + unsigned char bits = 0; + int nIsFirstCommand = 1; + const int nIsInverted = (nFlags & FLG_IS_INVERTED) && !(nFlags & FLG_IS_BACKWARD); + const int nIsBackward = (nFlags & FLG_IS_BACKWARD) ? 1 : 0; + int nDecompressedSize = 0; + + if (pInputData >= pInputDataEnd) + return -1; + + while (1) { + unsigned int nIsMatchWithOffset; + + if (nIsFirstCommand) { + /* The first command is always literals */ + nIsFirstCommand = 0; + nIsMatchWithOffset = 0; + } + else { + /* Read match with offset / literals bit */ + nIsMatchWithOffset = salvador_read_bit(&pInputData, pInputDataEnd, &nCurBitMask, &bits); + if (nIsMatchWithOffset == -1) + return -1; + } + + if (nIsMatchWithOffset == 0) { + unsigned int nLiterals = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + + /* Count literals */ + + if ((pInputData + nLiterals) <= pInputDataEnd) { + pInputData += nLiterals; + nDecompressedSize += nLiterals; + } + else { + return -1; + } + + /* Read match with offset / rep match bit */ + + nIsMatchWithOffset = salvador_read_bit(&pInputData, pInputDataEnd, &nCurBitMask, &bits); + if (nIsMatchWithOffset == -1) + return -1; + } + + unsigned int nMatchLen; + + if (nIsMatchWithOffset) { + /* Match with offset */ + + unsigned int nMatchOffsetHighByte; + + if (nIsInverted) + nMatchOffsetHighByte = salvador_read_elias_inverted(&pInputData, pInputDataEnd, 1, &nCurBitMask, &bits); + else + nMatchOffsetHighByte = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + + if (nMatchOffsetHighByte == 256) + break; + + if (pInputData >= pInputDataEnd) + return -1; + + unsigned int nMatchOffsetLowByte = (unsigned int)(*pInputData++); + + nMatchLen = salvador_read_elias_prefix(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits, nMatchOffsetLowByte & 1); + + nMatchLen += (2 - 1); + } + else { + /* Rep-match */ + + nMatchLen = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + } + + /* Count matched bytes */ + nDecompressedSize += nMatchLen; + } + + return nDecompressedSize; +} + +/** + * Decompress data in memory + * + * @param pInputData compressed data + * @param pOutData buffer for decompressed data + * @param nInputSize compressed size in bytes + * @param nMaxOutBufferSize maximum capacity of decompression buffer + * @param nDictionarySize size of dictionary in front of input data (0 for none) + * @param nFlags compression flags (set to FLG_IS_INVERTED) + * + * @return actual decompressed size, or -1 for error + */ +size_t salvador_decompress(const unsigned char *pInputData, unsigned char *pOutData, size_t nInputSize, size_t nMaxOutBufferSize, size_t nDictionarySize, const unsigned int nFlags) { + const unsigned char *pInputDataEnd = pInputData + nInputSize; + unsigned char *pCurOutData = pOutData + nDictionarySize; + const unsigned char *pOutDataEnd = pCurOutData + nMaxOutBufferSize; + int nCurBitMask = 0; + unsigned char bits = 0; + int nMatchOffset = 1; + int nIsFirstCommand = 1; + const int nIsInverted = (nFlags & FLG_IS_INVERTED) && !(nFlags & FLG_IS_BACKWARD); + const int nIsBackward = (nFlags & FLG_IS_BACKWARD) ? 1 : 0; + + if (pInputData >= pInputDataEnd && pCurOutData < pOutDataEnd) + return -1; + + while (1) { + unsigned int nIsMatchWithOffset; + + if (nIsFirstCommand) { + /* The first command is always literals */ + nIsFirstCommand = 0; + nIsMatchWithOffset = 0; + } + else { + /* Read match with offset / literals bit */ + nIsMatchWithOffset = salvador_read_bit(&pInputData, pInputDataEnd, &nCurBitMask, &bits); + if (nIsMatchWithOffset == -1) + return -1; + } + + if (nIsMatchWithOffset == 0) { + unsigned int nLiterals = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + + /* Copy literals */ + + if ((pInputData + nLiterals) <= pInputDataEnd && + (pCurOutData + nLiterals) <= pOutDataEnd) { + memcpy(pCurOutData, pInputData, nLiterals); + pInputData += nLiterals; + pCurOutData += nLiterals; + } + else { + return -1; + } + + /* Read match with offset / rep match bit */ + + nIsMatchWithOffset = salvador_read_bit(&pInputData, pInputDataEnd, &nCurBitMask, &bits); + if (nIsMatchWithOffset == -1) + return -1; + } + + unsigned int nMatchLen; + + if (nIsMatchWithOffset) { + /* Match with offset */ + + unsigned int nMatchOffsetHighByte; + + if (nIsInverted) + nMatchOffsetHighByte = salvador_read_elias_inverted(&pInputData, pInputDataEnd, 1, &nCurBitMask, &bits); + else + nMatchOffsetHighByte = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + + if (nMatchOffsetHighByte == 256) + break; + nMatchOffsetHighByte--; + + if (pInputData >= pInputDataEnd) + return -1; + + unsigned int nMatchOffsetLowByte = (unsigned int)(*pInputData++); + if (nIsBackward) + nMatchOffset = (nMatchOffsetHighByte << 7) | (nMatchOffsetLowByte >> 1); + else + nMatchOffset = (nMatchOffsetHighByte << 7) | (127 - (nMatchOffsetLowByte >> 1)); + nMatchOffset++; + + nMatchLen = salvador_read_elias_prefix(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits, nMatchOffsetLowByte & 1); + + nMatchLen += (2 - 1); + } + else { + /* Rep-match */ + + nMatchLen = salvador_read_elias(&pInputData, pInputDataEnd, 1, nIsBackward, &nCurBitMask, &bits); + } + + /* Copy matched bytes */ + const unsigned char* pSrc = pCurOutData - nMatchOffset; + if (pSrc >= pOutData) { + if ((pSrc + nMatchLen) <= pOutDataEnd) { + if ((pCurOutData + nMatchLen) <= pOutDataEnd) { + while (nMatchLen) { + *pCurOutData++ = *pSrc++; + nMatchLen--; + } + } + else { + return -1; + } + } + else { + return -1; + } + } + else { + return -1; + } + } + + return (size_t)(pCurOutData - pOutData) - nDictionarySize; +} diff --git a/gbdk-support/gbcompress/zx0/zx0_expand.h b/gbdk-support/gbcompress/zx0/zx0_expand.h @@ -0,0 +1,69 @@ +/* + * expand.h - decompressor definitions + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#ifndef _EXPAND_H +#define _EXPAND_H + +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get maximum decompressed size of compressed data + * + * @param pInputData compressed data + * @param nInputSize compressed size in bytes + * @param nFlags compression flags (set to FLG_IS_INVERTED) + * + * @return maximum decompressed size + */ +size_t salvador_get_max_decompressed_size(const unsigned char *pInputData, size_t nInputSize, const unsigned int nFlags); + +/** + * Decompress data in memory + * + * @param pInputData compressed data + * @param pOutData buffer for decompressed data + * @param nInputSize compressed size in bytes + * @param nMaxOutBufferSize maximum capacity of decompression buffer + * @param nDictionarySize size of dictionary in front of input data (0 for none) + * @param nFlags compression flags (set to FLG_IS_INVERTED) + * + * @return actual decompressed size, or -1 for error + */ +size_t salvador_decompress(const unsigned char *pInputData, unsigned char *pOutData, size_t nInputSize, size_t nMaxOutBufferSize, size_t nDictionarySize, const unsigned int nFlags); + +#ifdef __cplusplus +} +#endif + +#endif /* _EXPAND_H */ diff --git a/gbdk-support/gbcompress/zx0/zx0_format.h b/gbdk-support/gbcompress/zx0/zx0_format.h @@ -0,0 +1,43 @@ +/* + * format.h - byte stream format definitions + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#ifndef _FORMAT_H +#define _FORMAT_H + +#define MIN_OFFSET 1 +#define MAX_OFFSET 0x7f80 + +#define MAX_VARLEN 0xffff + +#define BLOCK_SIZE 0x10000 + +#define MIN_MATCH_SIZE 1 + +#endif /* _FORMAT_H */ diff --git a/gbdk-support/gbcompress/zx0/zx0_libsalvador.h b/gbdk-support/gbcompress/zx0/zx0_libsalvador.h @@ -0,0 +1,41 @@ +/* + * libsalvador.h - library definitions + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#ifndef _LIB_SALVADOR_H +#define _LIB_SALVADOR_H + +#include "zx0_format.h" +#include "zx0_shrink.h" +#include "zx0_expand.h" + +#define FLG_IS_INVERTED 1 /**< Use inverted (V2) format */ +#define FLG_IS_BACKWARD 2 /**< Use backward encoding */ + +#endif /* _LIB_SALVADOR_H */ diff --git a/gbdk-support/gbcompress/zx0/zx0_matchfinder.c b/gbdk-support/gbcompress/zx0/zx0_matchfinder.c @@ -0,0 +1,392 @@ +/* + * matchfinder.c - LZ match finder implementation + * + * The following copying information applies to this specific source code file: + * + * Written in 2019-2021 by Emmanuel Marty <marty.emmanuel@gmail.com> + * Portions written in 2014-2015 by Eric Biggers <ebiggers3@gmail.com> + * + * To the extent possible under law, the author(s) have dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide via the Creative Commons Zero 1.0 Universal Public Domain + * Dedication (the "CC0"). + * + * This software is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the CC0 for more details. + * + * You should have received a copy of the CC0 along with this software; if not + * see <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#include <stdlib.h> +#include <string.h> +#include "zx0_matchfinder.h" +#include "zx0_format.h" +#include "zx0_libsalvador.h" + +/** + * Hash index into TAG_BITS + * + * @param nIndex index value + * + * @return hash + */ +static inline int salvador_get_index_tag(unsigned int nIndex) { + return (int)(((unsigned long long)nIndex * 11400714819323198485ULL) >> (64ULL - TAG_BITS)); +} + +/** + * Parse input data, build suffix array and overlaid data structures to speed up match finding + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return 0 for success, non-zero for failure + */ +int salvador_build_suffix_array(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nInWindowSize) { + unsigned long long *intervals = pCompressor->intervals; + + /* Build suffix array from input data */ + saidx_t *suffixArray = (saidx_t*)intervals; + if (divsufsort_build_array(&pCompressor->divsufsort_context, pInWindow, suffixArray, nInWindowSize) != 0) { + return 100; + } + + int i, r; + + for (i = nInWindowSize - 1; i >= 0; i--) { + intervals[i] = suffixArray[i]; + } + + int *PLCP = (int*)pCompressor->pos_data; /* Use temporarily */ + int *Phi = PLCP; + int nCurLen = 0; + + /* Compute the permuted LCP first (Kärkkäinen method) */ + Phi[intervals[0]] = -1; + for (i = 1; i < nInWindowSize; i++) + Phi[intervals[i]] = (unsigned int)intervals[i - 1]; + for (i = 0; i < nInWindowSize; i++) { + if (Phi[i] == -1) { + PLCP[i] = 0; + continue; + } + const int nMaxLen = (i > Phi[i]) ? (nInWindowSize - i) : (nInWindowSize - Phi[i]); + while (nCurLen < nMaxLen && pInWindow[i + nCurLen] == pInWindow[Phi[i] + nCurLen]) nCurLen++; + PLCP[i] = nCurLen; + if (nCurLen > 0) + nCurLen--; + } + + /* Rotate permuted LCP into the LCP. This has better cache locality than the direct Kasai LCP method. This also + * saves us from having to build the inverse suffix array index, as the LCP is calculated without it using this method, + * and the interval builder below doesn't need it either. */ + intervals[0] &= POS_MASK; + + for (i = 1; i < nInWindowSize; i++) { + const int nIndex = (const int)(intervals[i] & POS_MASK); + int nLen = PLCP[nIndex]; + if (nLen < MIN_MATCH_SIZE) + nLen = 0; + if (nLen > LCP_MAX) + nLen = LCP_MAX; + int nTaggedLen = 0; + if (nLen) + nTaggedLen = (nLen << TAG_BITS) | (salvador_get_index_tag((unsigned int)nIndex) & ((1 << TAG_BITS) - 1)); + intervals[i] = ((unsigned long long)nIndex) | (((unsigned long long)nTaggedLen) << LCP_SHIFT); + } + + /** + * Build intervals for finding matches + * + * Methodology and code fragment taken from wimlib (CC0 license): + * https://wimlib.net/git/?p=wimlib;a=blob_plain;f=src/lcpit_matchfinder.c;h=a2d6a1e0cd95200d1f3a5464d8359d5736b14cbe;hb=HEAD + */ + unsigned long long * const SA_and_LCP = intervals; + unsigned long long *pos_data = pCompressor->pos_data; + unsigned long long next_interval_idx; + unsigned long long *top = pCompressor->open_intervals; + unsigned long long prev_pos = SA_and_LCP[0] & POS_MASK; + + *top = 0; + intervals[0] = 0; + next_interval_idx = 1; + + for (r = 1; r < nInWindowSize; r++) { + const unsigned long long next_pos = SA_and_LCP[r] & POS_MASK; + const unsigned long long next_lcp = SA_and_LCP[r] & LCP_MASK; + const unsigned long long top_lcp = *top & LCP_MASK; + + if (next_lcp == top_lcp) { + /* Continuing the deepest open interval */ + pos_data[prev_pos] = *top; + } + else if (next_lcp > top_lcp) { + /* Opening a new interval */ + *++top = next_lcp | next_interval_idx++; + pos_data[prev_pos] = *top; + } + else { + /* Closing the deepest open interval */ + pos_data[prev_pos] = *top; + for (;;) { + const unsigned long long closed_interval_idx = *top-- & POS_MASK; + const unsigned long long superinterval_lcp = *top & LCP_MASK; + + if (next_lcp == superinterval_lcp) { + /* Continuing the superinterval */ + intervals[closed_interval_idx] = *top; + break; + } + else if (next_lcp > superinterval_lcp) { + /* Creating a new interval that is a + * superinterval of the one being + * closed, but still a subinterval of + * its superinterval */ + *++top = next_lcp | next_interval_idx++; + intervals[closed_interval_idx] = *top; + break; + } + else { + /* Also closing the superinterval */ + intervals[closed_interval_idx] = *top; + } + } + } + prev_pos = next_pos; + } + + /* Close any still-open intervals. */ + pos_data[prev_pos] = *top; + for (; top > pCompressor->open_intervals; top--) + intervals[*top & POS_MASK] = *(top - 1); + + /* Success */ + return 0; +} + +/** + * Find matches at the specified offset in the input window + * + * @param pCompressor compression context + * @param nOffset offset to find matches at, in the input window + * @param pMatches pointer to returned matches + * @param pMatchDepth pointer to returned match depths + * @param nMaxMatches maximum number of matches to return (0 for none) + * + * @return number of matches + */ +static int salvador_find_matches_at(salvador_compressor *pCompressor, const int nOffset, salvador_match *pMatches, unsigned short *pMatchDepth, const int nMaxMatches) { + unsigned long long *intervals = pCompressor->intervals; + unsigned long long *pos_data = pCompressor->pos_data; + unsigned long long ref; + unsigned long long super_ref; + unsigned long long match_pos; + salvador_match *matchptr; + unsigned short *depthptr; + unsigned int nPrevOffset, nPrevLen, nCurDepth; + unsigned short* cur_depth; + const salvador_match* pMaxMatch = pMatches + nMaxMatches; + const unsigned int nMaxOffset = (const unsigned int)pCompressor->max_offset; + + /** + * Find matches using intervals + * + * Taken from wimlib (CC0 license): + * https://wimlib.net/git/?p=wimlib;a=blob_plain;f=src/lcpit_matchfinder.c;h=a2d6a1e0cd95200d1f3a5464d8359d5736b14cbe;hb=HEAD + */ + + /* Get the deepest lcp-interval containing the current suffix. */ + ref = pos_data[nOffset]; + + pos_data[nOffset] = 0; + + /* Ascend until we reach a visited interval, the root, or a child of the + * root. Link unvisited intervals to the current suffix as we go. */ + while ((super_ref = intervals[ref & POS_MASK]) & LCP_MASK) { + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + ref = super_ref; + } + + if (super_ref == 0) { + /* In this case, the current interval may be any of: + * (1) the root; + * (2) an unvisited child of the root */ + + if (ref != 0) /* Not the root? */ + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + return 0; + } + + /* Ascend indirectly via pos_data[] links. */ + + match_pos = super_ref & EXCL_VISITED_MASK; + matchptr = pMatches; + depthptr = pMatchDepth; + + nPrevOffset = 0; + nPrevLen = 0; + nCurDepth = 0; + cur_depth = NULL; + + if (matchptr < pMaxMatch) { + const unsigned int nMatchOffset = (const unsigned int)(nOffset - match_pos); + + if (nMatchOffset <= nMaxOffset) { + const unsigned int nMatchLen = (const unsigned int)(ref >> (LCP_SHIFT + TAG_BITS)); + + matchptr->length = nMatchLen; + matchptr->offset = nMatchOffset; + matchptr++; + + *depthptr = nCurDepth = 0; + cur_depth = depthptr++; + + nPrevLen = nMatchLen; + nPrevOffset = nMatchOffset; + } + } + + for (;;) { + if ((super_ref = pos_data[match_pos]) > ref) { + match_pos = intervals[super_ref & POS_MASK] & EXCL_VISITED_MASK; + + if (matchptr < pMaxMatch) { + const unsigned int nMatchOffset = (const unsigned int)(nOffset - match_pos); + + if (nMatchOffset <= nMaxOffset) { + const unsigned int nMatchLen = (const unsigned int)(ref >> (LCP_SHIFT + TAG_BITS)); + + if (nPrevLen > 2 && nPrevOffset && nMatchOffset == (nPrevOffset - 1) && nMatchLen == (nPrevLen - 1) && cur_depth && nCurDepth < LCP_MAX) { + *cur_depth = ++nCurDepth; + } + else { + matchptr->length = nMatchLen; + matchptr->offset = nMatchOffset; + matchptr++; + + *depthptr = nCurDepth = 0; + cur_depth = depthptr++; + } + + nPrevLen = nMatchLen; + nPrevOffset = nMatchOffset; + } + } + } + + while ((super_ref = pos_data[match_pos]) > ref) + match_pos = intervals[super_ref & POS_MASK] & EXCL_VISITED_MASK; + + intervals[ref & POS_MASK] = nOffset | VISITED_FLAG; + pos_data[match_pos] = (unsigned long long)ref; + + if (matchptr < pMaxMatch) { + const unsigned int nMatchOffset = (const unsigned int)(nOffset - match_pos); + + if (nMatchOffset <= nMaxOffset && nMatchOffset != nPrevOffset) { + const unsigned int nMatchLen = (const unsigned int)(ref >> (LCP_SHIFT + TAG_BITS)); + + if (nPrevLen > 2 && nPrevOffset && nMatchOffset == (nPrevOffset - 1) && nMatchLen == (nPrevLen - 1) && cur_depth && nCurDepth < LCP_MAX) { + *cur_depth = ++nCurDepth; + } + else { + matchptr->length = nMatchLen; + matchptr->offset = nMatchOffset; + matchptr++; + + *depthptr = nCurDepth = 0; + cur_depth = depthptr++; + } + + nPrevLen = nMatchLen; + nPrevOffset = nMatchOffset; + } + } + + if (super_ref == 0) + break; + ref = super_ref; + match_pos = intervals[ref & POS_MASK] & EXCL_VISITED_MASK; + + if (matchptr < pMaxMatch) { + const unsigned int nMatchOffset = (const unsigned int)(nOffset - match_pos); + + if (nMatchOffset <= nMaxOffset) { + const unsigned int nMatchLen = (const unsigned int)(ref >> (LCP_SHIFT + TAG_BITS)); + + if (nPrevLen > 2 && nPrevOffset && nMatchOffset == (nPrevOffset - 1) && nMatchLen == (nPrevLen - 1) && cur_depth && nCurDepth < LCP_MAX) { + *cur_depth = ++nCurDepth; + } + else { + matchptr->length = nMatchLen; + matchptr->offset = nMatchOffset; + matchptr++; + + *depthptr = nCurDepth = 0; + cur_depth = depthptr++; + } + + nPrevLen = nMatchLen; + nPrevOffset = nMatchOffset; + } + } + } + + return (int)(matchptr - pMatches); +} + +/** + * Skip previously compressed bytes + * + * @param pCompressor compression context + * @param nStartOffset current offset in input window (typically 0) + * @param nEndOffset offset to skip to in input window (typically the number of previously compressed bytes) + */ +void salvador_skip_matches(salvador_compressor *pCompressor, const int nStartOffset, const int nEndOffset) { + salvador_match match; + unsigned short depth; + int i; + + /* Skipping still requires scanning for matches, as this also performs a lazy update of the intervals. However, + * we don't store the matches. */ + for (i = nStartOffset; i < nEndOffset; i++) { + salvador_find_matches_at(pCompressor, i, &match, &depth, 0); + } +} + +/** + * Find all matches for the data to be compressed + * + * @param pCompressor compression context + * @param nMatchesPerOffset maximum number of matches to store for each offset + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + */ +void salvador_find_all_matches(salvador_compressor *pCompressor, const int nMatchesPerOffset, const int nStartOffset, const int nEndOffset) { + salvador_match *pMatch = pCompressor->match; + unsigned short *pMatchDepth = pCompressor->match_depth; + int i; + + for (i = nStartOffset; i < nEndOffset; i++) { + const int nMatches = salvador_find_matches_at(pCompressor, i, pMatch, pMatchDepth, nMatchesPerOffset); + + if (nMatches < nMatchesPerOffset) { + memset(pMatch + nMatches, 0, (nMatchesPerOffset - nMatches) * sizeof(salvador_match)); + memset(pMatchDepth + nMatches, 0, (nMatchesPerOffset - nMatches) * sizeof(unsigned short)); + } + + pMatch += nMatchesPerOffset; + pMatchDepth += nMatchesPerOffset; + } +} diff --git a/gbdk-support/gbcompress/zx0/zx0_matchfinder.h b/gbdk-support/gbcompress/zx0/zx0_matchfinder.h @@ -0,0 +1,74 @@ +/* + * matchfinder.h - LZ match finder definitions + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#ifndef _MATCHFINDER_H +#define _MATCHFINDER_H + +#include "zx0_shrink.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Parse input data, build suffix array and overlaid data structures to speed up match finding + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nInWindowSize total input size in bytes (previously compressed bytes + bytes to compress) + * + * @return 0 for success, non-zero for failure + */ +int salvador_build_suffix_array(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nInWindowSize); + +/** + * Skip previously compressed bytes + * + * @param pCompressor compression context + * @param nStartOffset current offset in input window (typically 0) + * @param nEndOffset offset to skip to in input window (typically the number of previously compressed bytes) + */ +void salvador_skip_matches(salvador_compressor *pCompressor, const int nStartOffset, const int nEndOffset); + +/** + * Find all matches for the data to be compressed + * + * @param pCompressor compression context + * @param nMatchesPerOffset maximum number of matches to store for each offset + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + */ +void salvador_find_all_matches(salvador_compressor *pCompressor, const int nMatchesPerOffset, const int nStartOffset, const int nEndOffset); + +#ifdef __cplusplus +} +#endif + +#endif /* _MATCHFINDER_H */ diff --git a/gbdk-support/gbcompress/zx0/zx0_salvador_c_UNUSED b/gbdk-support/gbcompress/zx0/zx0_salvador_c_UNUSED @@ -0,0 +1,1267 @@ +/* + * salvador.c - command line compression utility for the salvador library + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef _WIN32 +#include <windows.h> +#include <sys/timeb.h> +#else +#include <sys/time.h> +#endif +#include "libsalvador.h" + +#define OPT_VERBOSE 1 +#define OPT_STATS 2 +#define OPT_BACKWARD 4 +#define OPT_CLASSIC 8 + +#define TOOL_VERSION "1.4.2" + +/*---------------------------------------------------------------------------*/ + +#ifdef _WIN32 +LARGE_INTEGER hpc_frequency; +BOOL hpc_available = FALSE; +#endif + +static void do_init_time() { +#ifdef _WIN32 + hpc_frequency.QuadPart = 0; + hpc_available = QueryPerformanceFrequency(&hpc_frequency); +#endif +} + +static long long do_get_time() { + long long nTime; + +#ifdef _WIN32 + if (hpc_available) { + LARGE_INTEGER nCurTime; + + /* Use HPC hardware for best precision */ + QueryPerformanceCounter(&nCurTime); + nTime = (long long)(nCurTime.QuadPart * 1000000LL / hpc_frequency.QuadPart); + } + else { + struct _timeb tb; + _ftime(&tb); + + nTime = ((long long)tb.time * 1000LL + (long long)tb.millitm) * 1000LL; + } +#else + struct timeval tm; + gettimeofday(&tm, NULL); + + nTime = (long long)tm.tv_sec * 1000000LL + (long long)tm.tv_usec; +#endif + return nTime; +} + +static void do_reverse_buffer(unsigned char *pBuffer, size_t nBufferSize) { + size_t nMidPoint = nBufferSize / 2; + size_t i, j; + + for (i = 0, j = nBufferSize - 1; i < nMidPoint; i++, j--) { + unsigned char c = pBuffer[i]; + pBuffer[i] = pBuffer[j]; + pBuffer[j] = c; + } +} + +/*---------------------------------------------------------------------------*/ + +static void compression_progress(long long nOriginalSize, long long nCompressedSize) { + if (nOriginalSize >= 512 * 1024) { + fprintf(stdout, "\r%lld => %lld (%g %%) \b\b\b\b\b", nOriginalSize, nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize)); + fflush(stdout); + } +} + +static int do_compress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const unsigned int nMaxWindowSize) { + long long nStartTime = 0LL, nEndTime = 0LL; + size_t nOriginalSize = 0L, nCompressedSize = 0L, nMaxCompressedSize; + int nFlags = (nOptions & OPT_CLASSIC) ? 0 : FLG_IS_INVERTED; + salvador_stats stats; + unsigned char *pDecompressedData; + unsigned char *pCompressedData; + + if (nOptions & OPT_BACKWARD) + nFlags |= FLG_IS_BACKWARD; + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + FILE* f_dict = NULL; + size_t nDictionarySize = 0; + if (pszDictionaryFilename) { + /* Open the dictionary */ + f_dict = fopen(pszDictionaryFilename, "rb"); + if (!f_dict) { + fprintf(stderr, "error opening dictionary '%s' for reading\n", pszDictionaryFilename); + return 100; + } + + /* Get dictionary size */ + fseek(f_dict, 0, SEEK_END); + nDictionarySize = (size_t)ftell(f_dict); + fseek(f_dict, 0, SEEK_SET); + + if (nDictionarySize > BLOCK_SIZE) nDictionarySize = BLOCK_SIZE; + } + + /* Read the whole original file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + if (f_dict) fclose(f_dict); + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nOriginalSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pDecompressedData = (unsigned char*)malloc(nDictionarySize + nOriginalSize); + if (!pDecompressedData) { + fclose(f_in); + if (f_dict) fclose(f_dict); + fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nOriginalSize); + return 100; + } + + if (f_dict) { + /* Read dictionary data */ + if (fread(pDecompressedData + ((nOptions & OPT_BACKWARD) ? nOriginalSize : 0), 1, nDictionarySize, f_dict) != nDictionarySize) { + free(pDecompressedData); + fclose(f_in); + fclose(f_dict); + fprintf(stderr, "I/O error while reading dictionary '%s'\n", pszDictionaryFilename); + return 100; + } + + fclose(f_dict); + f_dict = NULL; + } + + /* Read input file data */ + if (fread(pDecompressedData + ((nOptions & OPT_BACKWARD) ? 0 : nDictionarySize), 1, nOriginalSize, f_in) != nOriginalSize) { + free(pDecompressedData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + f_in = NULL; + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData, nDictionarySize + nOriginalSize); + + /* Allocate max compressed size */ + + nMaxCompressedSize = salvador_get_max_compressed_size(nDictionarySize + nOriginalSize); + + pCompressedData = (unsigned char*)malloc(nMaxCompressedSize); + if (!pCompressedData) { + free(pDecompressedData); + fprintf(stderr, "out of memory for compressing '%s', %zu bytes needed\n", pszInFilename, nMaxCompressedSize); + return 100; + } + + memset(pCompressedData, 0, nMaxCompressedSize); + + nCompressedSize = salvador_compress(pDecompressedData, pCompressedData, nDictionarySize + nOriginalSize, nMaxCompressedSize, nFlags, nMaxWindowSize, nDictionarySize, compression_progress, &stats); + + if (nOptions & OPT_VERBOSE) { + nEndTime = do_get_time(); + } + + if (nCompressedSize == (size_t)-1) { + free(pCompressedData); + free(pDecompressedData); + fprintf(stderr, "compression error for '%s'\n", pszInFilename); + return 100; + } + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pCompressedData, nCompressedSize); + + /* Write whole compressed file out */ + + FILE *f_out = fopen(pszOutFilename, "wb"); + if (!f_out) { + free(pCompressedData); + free(pDecompressedData); + fprintf(stderr, "error opening '%s' for writing\n", pszOutFilename); + return 100; + } + + fwrite(pCompressedData, 1, nCompressedSize, f_out); + fclose(f_out); + + free(pCompressedData); + free(pDecompressedData); + + if (nOptions & OPT_VERBOSE) { + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "\rCompressed '%s' in %g seconds, %.02g Mb/s, %d tokens (%g bytes/token), %zu into %zu bytes ==> %g %%\n", + pszInFilename, fDelta, fSpeed, stats.commands_divisor, (double)nOriginalSize / (double)stats.commands_divisor, + nOriginalSize, nCompressedSize, (double)(nCompressedSize * 100.0 / nOriginalSize)); + } + + if (nOptions & OPT_STATS) { + if (stats.literals_divisor > 0) + fprintf(stdout, "Literals: min: %d avg: %d max: %d count: %d\n", stats.min_literals, stats.total_literals / stats.literals_divisor, stats.max_literals, stats.literals_divisor); + else + fprintf(stdout, "Literals: none\n"); + + fprintf(stdout, "Normal matches: %d rep matches: %d EOD: %d\n", + stats.num_normal_matches, stats.num_rep_matches, stats.num_eod); + + if (stats.match_divisor > 0) { + fprintf(stdout, "Offsets: min: %d avg: %d max: %d count: %d\n", stats.min_offset, (int)(stats.total_offsets / (long long)stats.match_divisor), stats.max_offset, stats.match_divisor); + fprintf(stdout, "Match lens: min: %d avg: %d max: %d count: %d\n", stats.min_match_len, stats.total_match_lens / stats.match_divisor, stats.max_match_len, stats.match_divisor); + } + else { + fprintf(stdout, "Offsets: none\n"); + fprintf(stdout, "Match lens: none\n"); + } + if (stats.rle1_divisor > 0) { + fprintf(stdout, "RLE1 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle1_len, stats.total_rle1_lens / stats.rle1_divisor, stats.max_rle1_len, stats.rle1_divisor); + } + else { + fprintf(stdout, "RLE1 lens: none\n"); + } + if (stats.rle2_divisor > 0) { + fprintf(stdout, "RLE2 lens: min: %d avg: %d max: %d count: %d\n", stats.min_rle2_len, stats.total_rle2_lens / stats.rle2_divisor, stats.max_rle2_len, stats.rle2_divisor); + } + else { + fprintf(stdout, "RLE2 lens: none\n"); + } + fprintf(stdout, "Safe distance: %d (0x%X)\n", stats.safe_dist, stats.safe_dist); + } + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_decompress(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) { + long long nStartTime = 0LL, nEndTime = 0LL; + size_t nCompressedSize, nMaxDecompressedSize, nOriginalSize; + unsigned char *pCompressedData; + unsigned char *pDecompressedData; + int nFlags = (nOptions & OPT_CLASSIC) ? 0 : FLG_IS_INVERTED; + + if (nOptions & OPT_BACKWARD) + nFlags |= FLG_IS_BACKWARD; + + /* Read the whole compressed file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nCompressedSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pCompressedData = (unsigned char*)malloc(nCompressedSize); + if (!pCompressedData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nCompressedSize); + return 100; + } + + if (fread(pCompressedData, 1, nCompressedSize, f_in) != nCompressedSize) { + free(pCompressedData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + f_in = NULL; + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pCompressedData, nCompressedSize); + + /* Get max decompressed size */ + + nMaxDecompressedSize = salvador_get_max_decompressed_size(pCompressedData, nCompressedSize, nFlags); + if (nMaxDecompressedSize == (size_t)-1) { + free(pCompressedData); + fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename); + return 100; + } + + FILE* f_dict = NULL; + size_t nDictionarySize = 0; + if (pszDictionaryFilename) { + /* Open the dictionary */ + f_dict = fopen(pszDictionaryFilename, "rb"); + if (!f_dict) { + fprintf(stderr, "error opening dictionary '%s' for reading\n", pszDictionaryFilename); + return 100; + } + + /* Get dictionary size */ + fseek(f_dict, 0, SEEK_END); + nDictionarySize = (size_t)ftell(f_dict); + fseek(f_dict, 0, SEEK_SET); + + if (nDictionarySize > BLOCK_SIZE) nDictionarySize = BLOCK_SIZE; + } + + /* Allocate max decompressed size */ + + pDecompressedData = (unsigned char*)malloc(nDictionarySize + nMaxDecompressedSize); + if (!pDecompressedData) { + free(pCompressedData); + if (f_dict) fclose(f_dict); + fprintf(stderr, "out of memory for decompressing '%s', %zu bytes needed\n", pszInFilename, nMaxDecompressedSize); + return 100; + } + + memset(pDecompressedData, 0, nDictionarySize + nMaxDecompressedSize); + + if (f_dict) { + /* Read dictionary data */ + if (fread(pDecompressedData, 1, nDictionarySize, f_dict) != nDictionarySize) { + free(pDecompressedData); + fclose(f_dict); + fprintf(stderr, "I/O error while reading dictionary '%s'\n", pszDictionaryFilename); + return 100; + } + + fclose(f_dict); + f_dict = NULL; + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData, nDictionarySize); + } + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + nOriginalSize = salvador_decompress(pCompressedData, pDecompressedData, nCompressedSize, nMaxDecompressedSize, nDictionarySize, nFlags); + if (nOriginalSize == (size_t)-1) { + free(pDecompressedData); + free(pCompressedData); + + fprintf(stderr, "decompression error for '%s'\n", pszInFilename); + return 100; + } + + if (nOptions & OPT_VERBOSE) { + nEndTime = do_get_time(); + } + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData + nDictionarySize, nOriginalSize); + + /* Write whole decompressed file out */ + + FILE *f_out = fopen(pszOutFilename, "wb"); + if (!f_out) { + free(pDecompressedData); + free(pCompressedData); + + fprintf(stderr, "error opening '%s' for writing\n", pszOutFilename); + return 100; + } + + fwrite(pDecompressedData + nDictionarySize, 1, nOriginalSize, f_out); + fclose(f_out); + + free(pDecompressedData); + free(pCompressedData); + + if (nOptions & OPT_VERBOSE) { + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "Decompressed '%s' in %g seconds, %g Mb/s\n", + pszInFilename, fDelta, fSpeed); + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_compare(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) { + long long nStartTime = 0LL, nEndTime = 0LL; + size_t nCompressedSize, nMaxDecompressedSize, nOriginalSize, nDecompressedSize; + unsigned char *pCompressedData = NULL; + unsigned char *pOriginalData = NULL; + unsigned char *pDecompressedData = NULL; + int nFlags = (nOptions & OPT_CLASSIC) ? 0 : FLG_IS_INVERTED; + + if (nOptions & OPT_BACKWARD) + nFlags |= FLG_IS_BACKWARD; + + /* Read the whole compressed file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nCompressedSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pCompressedData = (unsigned char*)malloc(nCompressedSize); + if (!pCompressedData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nCompressedSize); + return 100; + } + + if (fread(pCompressedData, 1, nCompressedSize, f_in) != nCompressedSize) { + free(pCompressedData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + f_in = NULL; + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pCompressedData, nCompressedSize); + + /* Read the whole original file in memory */ + + f_in = fopen(pszOutFilename, "rb"); + if (!f_in) { + free(pCompressedData); + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nOriginalSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pOriginalData = (unsigned char*)malloc(nOriginalSize); + if (!pOriginalData) { + fclose(f_in); + free(pCompressedData); + fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nOriginalSize); + return 100; + } + + if (fread(pOriginalData, 1, nOriginalSize, f_in) != nOriginalSize) { + free(pOriginalData); + fclose(f_in); + free(pCompressedData); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + f_in = NULL; + + /* Get max decompressed size */ + + nMaxDecompressedSize = salvador_get_max_decompressed_size(pCompressedData, nCompressedSize, nFlags); + if (nMaxDecompressedSize == (size_t)-1) { + free(pOriginalData); + free(pCompressedData); + fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename); + return 100; + } + + FILE* f_dict = NULL; + size_t nDictionarySize = 0; + if (pszDictionaryFilename) { + /* Open the dictionary */ + f_dict = fopen(pszDictionaryFilename, "rb"); + if (!f_dict) { + free(pOriginalData); + free(pCompressedData); + fprintf(stderr, "error opening dictionary '%s' for reading\n", pszDictionaryFilename); + return 100; + } + + /* Get dictionary size */ + fseek(f_dict, 0, SEEK_END); + nDictionarySize = (size_t)ftell(f_dict); + fseek(f_dict, 0, SEEK_SET); + + if (nDictionarySize > BLOCK_SIZE) nDictionarySize = BLOCK_SIZE; + } + + /* Allocate max decompressed size */ + + pDecompressedData = (unsigned char*)malloc(nDictionarySize + nMaxDecompressedSize); + if (!pDecompressedData) { + free(pOriginalData); + free(pCompressedData); + if (f_dict) fclose(f_dict); + fprintf(stderr, "out of memory for decompressing '%s', %zu bytes needed\n", pszInFilename, nMaxDecompressedSize); + return 100; + } + + memset(pDecompressedData, 0, nDictionarySize + nMaxDecompressedSize); + + if (f_dict) { + /* Read dictionary data */ + if (fread(pDecompressedData, 1, nDictionarySize, f_dict) != nDictionarySize) { + free(pDecompressedData); + free(pOriginalData); + free(pCompressedData); + fclose(f_dict); + fprintf(stderr, "I/O error while reading dictionary '%s'\n", pszDictionaryFilename); + return 100; + } + + fclose(f_dict); + f_dict = NULL; + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData, nDictionarySize); + } + + if (nOptions & OPT_VERBOSE) { + nStartTime = do_get_time(); + } + + nDecompressedSize = salvador_decompress(pCompressedData, pDecompressedData, nCompressedSize, nMaxDecompressedSize, nDictionarySize, nFlags); + if (nDecompressedSize == (size_t)-1) { + free(pDecompressedData); + free(pOriginalData); + free(pCompressedData); + + fprintf(stderr, "decompression error for '%s'\n", pszInFilename); + return 100; + } + + if (nOptions & OPT_VERBOSE) { + nEndTime = do_get_time(); + } + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData + nDictionarySize, nDecompressedSize); + + if (nDecompressedSize != nOriginalSize || memcmp(pDecompressedData + nDictionarySize, pOriginalData, nOriginalSize)) { + free(pDecompressedData); + free(pOriginalData); + free(pCompressedData); + + fprintf(stderr, "error comparing compressed file '%s' with original '%s'\n", pszInFilename, pszOutFilename); + return 100; + } + + free(pDecompressedData); + free(pOriginalData); + free(pCompressedData); + + if (nOptions & OPT_VERBOSE) { + double fDelta = ((double)(nEndTime - nStartTime)) / 1000000.0; + double fSpeed = ((double)nOriginalSize / 1048576.0) / fDelta; + fprintf(stdout, "Compared '%s' in %g seconds, %g Mb/s\n", + pszInFilename, fDelta, fSpeed); + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static void generate_compressible_data(unsigned char *pBuffer, size_t nBufferSize, unsigned int nSeed, int nNumLiteralValues, float fMatchProbability) { + size_t nIndex = 0; + int nMatchProbability = (int)(fMatchProbability * 1023.0f); + + srand(nSeed); + + if (nBufferSize == 0) return; + pBuffer[nIndex++] = rand() % nNumLiteralValues; + + while (nIndex < nBufferSize) { + if ((rand() & 1023) >= nMatchProbability) { + size_t nLiteralCount = rand() & 127; + if (nLiteralCount > (nBufferSize - nIndex)) + nLiteralCount = nBufferSize - nIndex; + + while (nLiteralCount--) + pBuffer[nIndex++] = rand() % nNumLiteralValues; + } + else { + size_t nMatchLength = MIN_MATCH_SIZE + (rand() & 1023); + size_t nMatchOffset; + + if (nMatchLength > (nBufferSize - nIndex)) + nMatchLength = nBufferSize - nIndex; + if (nMatchLength > nIndex) + nMatchLength = nIndex; + + if (nMatchLength < nIndex) + nMatchOffset = rand() % (nIndex - nMatchLength); + else + nMatchOffset = 0; + + while (nMatchLength--) { + pBuffer[nIndex] = pBuffer[nIndex - nMatchOffset]; + nIndex++; + } + } + } +} + +static void xor_data(unsigned char *pBuffer, size_t nBufferSize, unsigned int nSeed, float fXorProbability) { + size_t nIndex = 0; + int nXorProbability = (int)(fXorProbability * 1023.0f); + + srand(nSeed); + + while (nIndex < nBufferSize) { + if ((rand() & 1023) < nXorProbability) { + pBuffer[nIndex] ^= 0xff; + } + nIndex++; + } +} + +static int do_self_test(const unsigned int nOptions, const unsigned int nMaxWindowSize, const int nIsQuickTest) { + unsigned char *pGeneratedData; + unsigned char *pCompressedData; + unsigned char *pTmpCompressedData; + unsigned char *pTmpDecompressedData; + size_t nGeneratedDataSize; + size_t nMaxCompressedDataSize; + unsigned int nSeed = 123; + int nFlags = FLG_IS_INVERTED; + int i; + + if (nOptions & OPT_BACKWARD) + nFlags |= FLG_IS_BACKWARD; + + pGeneratedData = (unsigned char*)malloc(4 * BLOCK_SIZE); + if (!pGeneratedData) { + fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE); + return 100; + } + + nMaxCompressedDataSize = salvador_get_max_compressed_size(4 * BLOCK_SIZE); + pCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize); + if (!pCompressedData) { + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %zu bytes needed\n", nMaxCompressedDataSize); + return 100; + } + + pTmpCompressedData = (unsigned char*)malloc(nMaxCompressedDataSize); + if (!pTmpCompressedData) { + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %zu bytes needed\n", nMaxCompressedDataSize); + return 100; + } + + pTmpDecompressedData = (unsigned char*)malloc(4 * BLOCK_SIZE); + if (!pTmpDecompressedData) { + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "out of memory, %d bytes needed\n", 4 * BLOCK_SIZE); + return 100; + } + + memset(pGeneratedData, 0, 4 * BLOCK_SIZE); + memset(pCompressedData, 0, nMaxCompressedDataSize); + memset(pTmpCompressedData, 0, nMaxCompressedDataSize); + + /* Test compressing with a too small buffer to do anything, expect to fail cleanly */ + for (i = 0; i < 12; i++) { + generate_compressible_data(pGeneratedData, i, nSeed, 256, 0.5f); + salvador_compress(pGeneratedData, pCompressedData, i, i, nFlags, nMaxWindowSize, 0 /* dictionary size */, NULL, NULL); + } + + size_t nDataSizeStep = 128; + float fProbabilitySizeStep = nIsQuickTest ? 0.005f : 0.0005f; + + for (nGeneratedDataSize = 1024; nGeneratedDataSize <= (nIsQuickTest ? 1024U : (4U * BLOCK_SIZE)); nGeneratedDataSize += nDataSizeStep) { + float fMatchProbability; + + fprintf(stdout, "size %zu", nGeneratedDataSize); + for (fMatchProbability = 0; fMatchProbability <= 0.995f; fMatchProbability += fProbabilitySizeStep) { + int nNumLiteralValues[12] = { 1, 2, 3, 15, 30, 56, 96, 137, 178, 191, 255, 256 }; + float fXorProbability; + + fputc('.', stdout); + fflush(stdout); + + for (i = 0; i < 12; i++) { + /* Generate data to compress */ + generate_compressible_data(pGeneratedData, nGeneratedDataSize, nSeed, nNumLiteralValues[i], fMatchProbability); + + /* Try to compress it, expected to succeed */ + size_t nActualCompressedSize = salvador_compress(pGeneratedData, pCompressedData, nGeneratedDataSize, salvador_get_max_compressed_size(nGeneratedDataSize), + nFlags, nMaxWindowSize, 0 /* dictionary size */, NULL, NULL); + if (nActualCompressedSize == (size_t)-1 || nActualCompressedSize < (1 + 1 + 1 /* footer */)) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error compressing size %zu, seed %u, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + /* Try to decompress it, expected to succeed */ + size_t nActualDecompressedSize; + nActualDecompressedSize = salvador_decompress(pCompressedData, pTmpDecompressedData, nActualCompressedSize, nGeneratedDataSize, 0 /* dictionary size */, nFlags); + if (nActualDecompressedSize == (size_t)-1) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error decompressing size %zu, seed %u, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + if (memcmp(pGeneratedData, pTmpDecompressedData, nGeneratedDataSize)) { + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + free(pTmpCompressedData); + pTmpCompressedData = NULL; + free(pCompressedData); + pCompressedData = NULL; + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stderr, "\nself-test: error comparing decompressed and original data, size %zu, seed %u, match probability %f, literals range %d\n", nGeneratedDataSize, nSeed, fMatchProbability, nNumLiteralValues[i]); + return 100; + } + + /* Try to decompress corrupted data, expected to fail cleanly, without crashing or corrupting memory outside the output buffer */ + for (fXorProbability = 0.05f; fXorProbability <= 0.5f; fXorProbability += 0.05f) { + memcpy(pTmpCompressedData, pCompressedData, nActualCompressedSize); + xor_data(pTmpCompressedData, nActualCompressedSize, nSeed, fXorProbability); + salvador_decompress(pTmpCompressedData, pGeneratedData, nActualCompressedSize, nGeneratedDataSize, 0 /* dictionary size */, nFlags); + } + } + + nSeed++; + } + + fputc(10, stdout); + fflush(stdout); + + nDataSizeStep <<= 1; + if (nDataSizeStep > (128 * 4096)) + nDataSizeStep = 128 * 4096; + fProbabilitySizeStep *= 1.25; + if (fProbabilitySizeStep > (0.0005f * 4096)) + fProbabilitySizeStep = 0.0005f * 4096; + } + + free(pTmpDecompressedData); + pTmpDecompressedData = NULL; + + free(pTmpCompressedData); + pTmpCompressedData = NULL; + + free(pCompressedData); + pCompressedData = NULL; + + free(pGeneratedData); + pGeneratedData = NULL; + + fprintf(stdout, "All tests passed.\n"); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_compr_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions, const unsigned int nMaxWindowSize) { + size_t nFileSize, nMaxCompressedSize; + unsigned char *pFileData; + unsigned char *pCompressedData; + int nFlags = FLG_IS_INVERTED; + int i; + + if (pszDictionaryFilename) { + fprintf(stderr, "in-memory benchmarking does not support dictionaries\n"); + return 100; + } + + /* Read the whole original file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nFileSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pFileData = (unsigned char*)malloc(nFileSize); + if (!pFileData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nFileSize); + return 100; + } + + if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) { + free(pFileData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + f_in = NULL; + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pFileData, nFileSize); + + /* Allocate max compressed size */ + + nMaxCompressedSize = salvador_get_max_compressed_size(nFileSize); + + pCompressedData = (unsigned char*)malloc(nMaxCompressedSize + 2048); + if (!pCompressedData) { + free(pFileData); + fprintf(stderr, "out of memory for compressing '%s', %zu bytes needed\n", pszInFilename, nMaxCompressedSize); + return 100; + } + + memset(pCompressedData + 1024, 0, nMaxCompressedSize); + + long long nBestCompTime = -1; + + size_t nActualCompressedSize = 0; + size_t nRightGuardPos = nMaxCompressedSize; + + for (i = 0; i < 5; i++) { + unsigned char nGuard = 0x33 + i; + int j; + + /* Write guard bytes around the output buffer, to help check for writes outside of it by the compressor */ + memset(pCompressedData, nGuard, 1024); + memset(pCompressedData + 1024 + nRightGuardPos, nGuard, 1024); + + long long t0 = do_get_time(); + nActualCompressedSize = salvador_compress(pFileData, pCompressedData + 1024, nFileSize, nRightGuardPos, nFlags, nMaxWindowSize, 0 /* dictionary size */, NULL, NULL); + long long t1 = do_get_time(); + if (nActualCompressedSize == (size_t)-1) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "compression error\n"); + return 100; + } + + long long nCurDecTime = t1 - t0; + if (nBestCompTime == -1 || nBestCompTime > nCurDecTime) + nBestCompTime = nCurDecTime; + + /* Check guard bytes before the output buffer */ + for (j = 0; j < 1024; j++) { + if (pCompressedData[j] != nGuard) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j - 1024); + return 100; + } + } + + /* Check guard bytes after the output buffer */ + for (j = 0; j < 1024; j++) { + if (pCompressedData[1024 + nRightGuardPos + j] != nGuard) { + free(pCompressedData); + free(pFileData); + fprintf(stderr, "error, wrote outside of output buffer at %d!\n", j); + return 100; + } + } + + nRightGuardPos = nActualCompressedSize; + } + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pCompressedData + 1024, nActualCompressedSize); + + if (pszOutFilename) { + FILE *f_out; + + /* Write whole compressed file out */ + + f_out = fopen(pszOutFilename, "wb"); + if (f_out) { + fwrite(pCompressedData + 1024, 1, nActualCompressedSize, f_out); + fclose(f_out); + } + } + + free(pCompressedData); + free(pFileData); + + fprintf(stdout, "compressed size: %zu bytes\n", nActualCompressedSize); + fprintf(stdout, "compression time: %lld microseconds (%g Mb/s)\n", nBestCompTime, ((double)nActualCompressedSize / 1024.0) / ((double)nBestCompTime / 1000.0)); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int do_dec_benchmark(const char *pszInFilename, const char *pszOutFilename, const char *pszDictionaryFilename, const unsigned int nOptions) { + size_t nFileSize, nMaxDecompressedSize; + unsigned char *pFileData; + unsigned char *pDecompressedData; + int nFlags = FLG_IS_INVERTED; + int i; + + if (pszDictionaryFilename) { + fprintf(stderr, "in-memory benchmarking does not support dictionaries\n"); + return 100; + } + + /* Read the whole compressed file in memory */ + + FILE *f_in = fopen(pszInFilename, "rb"); + if (!f_in) { + fprintf(stderr, "error opening '%s' for reading\n", pszInFilename); + return 100; + } + + fseek(f_in, 0, SEEK_END); + nFileSize = (size_t)ftell(f_in); + fseek(f_in, 0, SEEK_SET); + + pFileData = (unsigned char*)malloc(nFileSize); + if (!pFileData) { + fclose(f_in); + fprintf(stderr, "out of memory for reading '%s', %zu bytes needed\n", pszInFilename, nFileSize); + return 100; + } + + if (fread(pFileData, 1, nFileSize, f_in) != nFileSize) { + free(pFileData); + fclose(f_in); + fprintf(stderr, "I/O error while reading '%s'\n", pszInFilename); + return 100; + } + + fclose(f_in); + f_in = NULL; + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pFileData, nFileSize); + + /* Allocate max decompressed size */ + + nMaxDecompressedSize = salvador_get_max_decompressed_size(pFileData, nFileSize, nFlags); + if (nMaxDecompressedSize == (size_t)-1) { + free(pFileData); + fprintf(stderr, "invalid compressed format for file '%s'\n", pszInFilename); + return 100; + } + + pDecompressedData = (unsigned char*)malloc(nMaxDecompressedSize); + if (!pDecompressedData) { + free(pFileData); + fprintf(stderr, "out of memory for decompressing '%s', %zu bytes needed\n", pszInFilename, nMaxDecompressedSize); + return 100; + } + + memset(pDecompressedData, 0, nMaxDecompressedSize); + + long long nBestDecTime = -1; + + size_t nActualDecompressedSize = 0; + for (i = 0; i < 50; i++) { + long long t0 = do_get_time(); + nActualDecompressedSize = salvador_decompress(pFileData, pDecompressedData, nFileSize, nMaxDecompressedSize, 0 /* dictionary size */, nFlags); + long long t1 = do_get_time(); + if (nActualDecompressedSize == (size_t)-1) { + free(pDecompressedData); + free(pFileData); + fprintf(stderr, "decompression error\n"); + return 100; + } + + long long nCurDecTime = t1 - t0; + if (nBestDecTime == -1 || nBestDecTime > nCurDecTime) + nBestDecTime = nCurDecTime; + } + + if (nOptions & OPT_BACKWARD) + do_reverse_buffer(pDecompressedData, nActualDecompressedSize); + + if (pszOutFilename) { + FILE *f_out; + + /* Write whole decompressed file out */ + + f_out = fopen(pszOutFilename, "wb"); + if (f_out) { + fwrite(pDecompressedData, 1, nActualDecompressedSize, f_out); + fclose(f_out); + } + } + + free(pDecompressedData); + free(pFileData); + + fprintf(stdout, "decompressed size: %zu bytes\n", nActualDecompressedSize); + fprintf(stdout, "decompression time: %lld microseconds (%g Mb/s)\n", nBestDecTime, ((double)nActualDecompressedSize / 1024.0) / ((double)nBestDecTime / 1000.0)); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +int main(int argc, char **argv) { + int i; + const char *pszInFilename = NULL; + const char *pszOutFilename = NULL; + const char *pszDictionaryFilename = NULL; + int nArgsError = 0; + int nCommandDefined = 0; + int nVerifyCompression = 0; + char cCommand = 'z'; + unsigned int nOptions = 0; + unsigned int nMaxWindowSize = 0; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-d")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'd'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-z")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'z'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-c")) { + if (!nVerifyCompression) { + nVerifyCompression = 1; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-cbench")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'B'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-dbench")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'b'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-test")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 't'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-quicktest")) { + if (!nCommandDefined) { + nCommandDefined = 1; + cCommand = 'T'; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-D")) { + if (!pszDictionaryFilename && (i + 1) < argc) { + pszDictionaryFilename = argv[i + 1]; + i++; + } + else + nArgsError = 1; + } + else if (!strncmp(argv[i], "-D", 2)) { + if (!pszDictionaryFilename) { + pszDictionaryFilename = argv[i] + 2; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-v")) { + if ((nOptions & OPT_VERBOSE) == 0) { + nOptions |= OPT_VERBOSE; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-w")) { + if (!nMaxWindowSize && (i + 1) < argc) { + char *pEnd = NULL; + nMaxWindowSize = (int)strtol(argv[i + 1], &pEnd, 10); + if (pEnd && pEnd != argv[i + 1] && (nMaxWindowSize >= 16 && nMaxWindowSize <= MAX_OFFSET)) { + i++; + } + else { + nArgsError = 1; + } + } + else + nArgsError = 1; + } + else if (!strncmp(argv[i], "-w", 2)) { + if (!nMaxWindowSize) { + char *pEnd = NULL; + nMaxWindowSize = (int)strtol(argv[i] + 2, &pEnd, 10); + if (!(pEnd && pEnd != (argv[i] + 2) && (nMaxWindowSize >= 16 && nMaxWindowSize <= MAX_OFFSET))) { + nArgsError = 1; + } + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-stats")) { + if ((nOptions & OPT_STATS) == 0) { + nOptions |= OPT_STATS; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-b")) { + if ((nOptions & OPT_BACKWARD) == 0) { + nOptions |= OPT_BACKWARD; + } + else + nArgsError = 1; + } + else if (!strcmp(argv[i], "-classic")) { + if ((nOptions & OPT_CLASSIC) == 0) { + nOptions |= OPT_CLASSIC; + } + else + nArgsError = 1; + } + else { + if (!pszInFilename) + pszInFilename = argv[i]; + else { + if (!pszOutFilename) + pszOutFilename = argv[i]; + else + nArgsError = 1; + } + } + } + + if (!nArgsError && cCommand == 't') { + return do_self_test(nOptions, nMaxWindowSize, 0); + } + else if (!nArgsError && cCommand == 'T') { + return do_self_test(nOptions, nMaxWindowSize, 1); + } + + if (nArgsError || !pszInFilename || !pszOutFilename) { + fprintf(stderr, "salvador command-line tool v" TOOL_VERSION " by Emmanuel Marty\n"); + fprintf(stderr, "usage: %s [-c] [-d] [-v] [-b] <infile> <outfile>\n", argv[0]); + fprintf(stderr, " -c: check resulting stream after compressing\n"); + fprintf(stderr, " -d: decompress (default: compress)\n"); + fprintf(stderr, " -b: backwards compression or decompression\n"); + fprintf(stderr, " -w <size>: maximum window size, in bytes (16..32639), defaults to maximum\n"); + fprintf(stderr, " -D <file>: use dictionary file\n"); + fprintf(stderr, " -cbench: benchmark in-memory compression\n"); + fprintf(stderr, " -dbench: benchmark in-memory decompression\n"); + fprintf(stderr, " -test: run full automated self-tests\n"); + fprintf(stderr, "-quicktest: run quick automated self-tests\n"); + fprintf(stderr, " -stats: show compressed data stats\n"); + fprintf(stderr, " -classic: encode and decode using classical (V1) format, defaults to modern (V2)\n"); + fprintf(stderr, " -v: be verbose\n"); + return 100; + } + + do_init_time(); + + if (cCommand == 'z') { + int nResult = do_compress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMaxWindowSize); + if (nResult == 0 && nVerifyCompression) { + return do_compare(pszOutFilename, pszInFilename, pszDictionaryFilename, nOptions); + } else { + return nResult; + } + } + else if (cCommand == 'd') { + return do_decompress(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions); + } + else if (cCommand == 'B') { + return do_compr_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions, nMaxWindowSize); + } + else if (cCommand == 'b') { + return do_dec_benchmark(pszInFilename, pszOutFilename, pszDictionaryFilename, nOptions); + } + else { + return 100; + } +} diff --git a/gbdk-support/gbcompress/zx0/zx0_shrink.c b/gbdk-support/gbcompress/zx0/zx0_shrink.c @@ -0,0 +1,2063 @@ +/* + * shrink.c - compressor implementation + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include "zx0_libsalvador.h" +#include "zx0_matchfinder.h" +#include "zx0_shrink.h" +#include "zx0_format.h" + +#define MIN_ENCODED_MATCH_SIZE 2 +#define TOKEN_SIZE 1 +#define OFFSET_COST(__offset) (((__offset) <= 128) ? 8 : (7 + salvador_get_elias_size((((__offset) - 1) >> 7) + 1))) + +/** Costs, per length */ +static const char salvador_cost_for_len[8192] = { + 0, 2, 4, 4, 6, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +}; + +/** + * Get the number of bits required to encode a gamma value + * + * @param nValue value to encode as gamma + * + * @return number of bits required for encoding + */ +static int salvador_get_elias_size(const int nValue) { + if (nValue >= 0 && nValue < 8192) { + return salvador_cost_for_len[nValue] - TOKEN_SIZE; + } + else { + int i = nValue; + int nBits = 0; + + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + i = (i - (i >> 1)); + + while ((i >>= 1) > 0) { + nBits++; + nBits++; + } + + nBits++; + + return nBits; + } +} + +/** + * Write packed 0 control bit to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_zero_ctrl_bit(unsigned char *pOutData, int nOutOffset, const int nMaxOutDataSize, int *nCurBitsOffset, int *nCurBitShift) { + if (nOutOffset >= 0) { + if ((*nCurBitShift) == -1) { + /* Allocate a new byte in the stream to pack bits in */ + if (nOutOffset >= nMaxOutDataSize) return -1; + (*nCurBitsOffset) = nOutOffset; + (*nCurBitShift) = 7; + pOutData[nOutOffset++] = 0; + } + + (*nCurBitShift)--; + } + + return nOutOffset; +} + +/** + * Write packed 1 control bit to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_one_ctrl_bit(unsigned char* pOutData, int nOutOffset, const int nMaxOutDataSize, int* nCurBitsOffset, int* nCurBitShift) { + if (nOutOffset >= 0) { + if ((*nCurBitShift) == -1) { + /* Allocate a new byte in the stream to pack bits in */ + if (nOutOffset >= nMaxOutDataSize) return -1; + (*nCurBitsOffset) = nOutOffset; + (*nCurBitShift) = 7; + pOutData[nOutOffset++] = 0; + } + + pOutData[(*nCurBitsOffset)] |= 1 << ((*nCurBitShift)--); + } + + return nOutOffset; +} + +/** + * Write packed data bit to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nValue bit value to write + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_data_bit(unsigned char* pOutData, const int nOutOffset, const int nMaxOutDataSize, const int nValue, const int* nCurBitsOffset, int* nCurBitShift) { + if (nOutOffset >= 0) { + pOutData[(*nCurBitsOffset)] |= nValue << ((*nCurBitShift)--); + } + + return nOutOffset; +} + +/** + * Write normally encoded, interlaced elias gamma value to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nValue value to write with gamma encoding + * @param nBackward 1 for backward compression, 0 for forward compression + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_normal_elias_value(unsigned char* pOutData, int nOutOffset, const int nMaxOutDataSize, const int nValue, const int nBackward, int* nCurBitsOffset, int* nCurBitShift) { + int i = nValue; + + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + i = (i - (i >> 1)); + + if (nBackward) { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 1 : 0, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } + else { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 1 : 0, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } +} + +/** + * Write inverted, interlaced elias gamma value to output (compressed) buffer + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nValue value to write with gamma encoding + * @param nBackward 1 for backward compression, 0 for forward compression + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_inverted_elias_value(unsigned char* pOutData, int nOutOffset, const int nMaxOutDataSize, const int nValue, const int nBackward, int* nCurBitsOffset, int* nCurBitShift) { + int i = nValue; + + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + i = (i - (i >> 1)); + + if (nBackward) { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 0 : 1, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } + else { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 0 : 1, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } +} + +/** + * Write elias gamma encoded value to output (compressed) buffer, with the first bit stored in a different (match offset) byte + * + * @param pOutData pointer to output buffer + * @param nOutOffset current write index into output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nValue value to write with gamma encoding + * @param nBackward 1 for backward compression, 0 for forward compression + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * + * @return updated write index into output buffer, or -1 in case of an error + */ +static int salvador_write_split_elias_value(unsigned char* pOutData, int nOutOffset, const int nMaxOutDataSize, const int nValue, const int nBackward, int* nCurBitsOffset, int* nCurBitShift) { + int i = nValue; + + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + i = (i - (i >> 1)); + + i >>= 1; + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 1 : 0, nCurBitsOffset, nCurBitShift); + + if (nBackward) { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 1 : 0, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } + else { + while ((i >>= 1) > 0) { + nOutOffset = salvador_write_zero_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, (nValue & i) ? 1 : 0, nCurBitsOffset, nCurBitShift); + } + + return salvador_write_one_ctrl_bit(pOutData, nOutOffset, nMaxOutDataSize, nCurBitsOffset, nCurBitShift); + } +} + +/** + * Get the number of extra bits required to represent a literals length + * + * @param nLength literals length + * + * @return number of extra bits required + */ +static inline int salvador_get_literals_varlen_size(const int nLength) { + if (nLength >= 0 && nLength < 8192) + return salvador_cost_for_len[nLength]; + else + return TOKEN_SIZE + salvador_get_elias_size(nLength); +} + +/** + * Get the number of extra bits required to represent a non-rep match length + * + * @param __nLength actual match length + * + * @return number of extra bits required + */ +#define salvador_get_match_varlen_size_norep(__nLength) salvador_get_elias_size((__nLength) - 1) + +/** + * Get the number of extra bits required to represent a repmatch length + * + * @param __nLength actual match length + * + * @return number of extra bits required + */ +#define salvador_get_match_varlen_size_rep(__nLength) salvador_get_elias_size(__nLength) + +/** + * Insert forward rep candidate + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param i input data window position whose matches are being considered + * @param nMatchOffset match offset to use as rep candidate + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nDepth current insertion depth + */ +static void salvador_insert_forward_match(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int i, const int nMatchOffset, const int nStartOffset, const int nEndOffset, const int nDepth) { + const salvador_arrival *arrival = pCompressor->arrival + ((i - nStartOffset) * pCompressor->max_arrivals_per_position); + const int *rle_len = (const int*)pCompressor->intervals /* reuse */; + salvador_visited* visited = ((salvador_visited*)pCompressor->pos_data) - nStartOffset /* reuse */; + int j; + + for (j = 0; j < NINITIAL_ARRIVALS_PER_POSITION && arrival[j].from_slot; j++) { + if (arrival[j].num_literals) { + const int nRepOffset = arrival[j].rep_offset; + + if (nMatchOffset != nRepOffset) { + const int nRepPos = arrival[j].rep_pos; + + if (nRepPos >= nStartOffset && + nRepPos < nEndOffset && + visited[nRepPos] != nMatchOffset) { + + visited[nRepPos] = nMatchOffset; + + salvador_match* fwd_match = pCompressor->match + ((nRepPos - nStartOffset) * NMATCHES_PER_INDEX); + + if (fwd_match[NMATCHES_PER_INDEX - 1].length == 0) { + if (nRepPos >= nMatchOffset) { + const unsigned char* pInWindowStart = pInWindow + nRepPos; + + if (pInWindowStart[0] == pInWindowStart[-nMatchOffset]) { + if (nRepOffset) { + unsigned int nMaxRepLen = nEndOffset - nRepPos; + + if (nMaxRepLen > LCP_MAX) + nMaxRepLen = LCP_MAX; + const unsigned char* pInWindowMax = pInWindowStart + nMaxRepLen; + + const int nLen0 = rle_len[nRepPos - nMatchOffset]; + const int nLen1 = rle_len[nRepPos]; + unsigned int nMinLen = (nLen0 < nLen1) ? nLen0 : nLen1, r; + + for (r = 0; fwd_match[r].length; r++) { + if (fwd_match[r].offset == nMatchOffset) { + if (nMinLen < fwd_match[r].length) + nMinLen = fwd_match[r].length; + break; + } + } + + const unsigned char* pInWindowAtRepOffset = pInWindowStart + nMinLen; + + if (pInWindowAtRepOffset > pInWindowMax) + pInWindowAtRepOffset = pInWindowMax; + + while ((pInWindowAtRepOffset + 8) < pInWindowMax && !memcmp(pInWindowAtRepOffset, pInWindowAtRepOffset - nMatchOffset, 8)) + pInWindowAtRepOffset += 8; + while ((pInWindowAtRepOffset + 4) < pInWindowMax && !memcmp(pInWindowAtRepOffset, pInWindowAtRepOffset - nMatchOffset, 4)) + pInWindowAtRepOffset += 4; + while (pInWindowAtRepOffset < pInWindowMax && pInWindowAtRepOffset[0] == pInWindowAtRepOffset[-nMatchOffset]) + pInWindowAtRepOffset++; + + const unsigned short nCurRepLen = (const unsigned short)(pInWindowAtRepOffset - pInWindowStart); + unsigned short* fwd_depth = pCompressor->match_depth + ((nRepPos - nStartOffset) * NMATCHES_PER_INDEX); + + if (!fwd_match[r].length) { + fwd_match[r].length = nCurRepLen; + fwd_match[r].offset = nMatchOffset; + fwd_depth[r] = 0; + + if (nDepth < 9) + salvador_insert_forward_match(pCompressor, pInWindow, nRepPos, nMatchOffset, nStartOffset, nEndOffset, nDepth + 1); + } + else { + if (fwd_match[r].length < nCurRepLen && fwd_depth[r] == 0) { + fwd_match[r].length = nCurRepLen; + } + } + } + } + } + } + } + } + } + } +} + +/** + * Attempt to pick optimal matches, so as to produce the smallest possible output that decompresses to the same input + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nInsertForwardReps non-zero to insert forward repmatch candidates, zero to use the previously inserted candidates + * @param nCurRepMatchOffset starting rep offset for this block + * @param nArrivalsPerPosition number of arrivals to record per input buffer position + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + */ +static void salvador_optimize_forward(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, const int nInsertForwardReps, const int *nCurRepMatchOffset, const int nArrivalsPerPosition, const int nBlockFlags) { + const int nMaxArrivalsPerPosition = pCompressor->max_arrivals_per_position; + salvador_arrival *arrival = pCompressor->arrival - (nStartOffset * nMaxArrivalsPerPosition); + const int* rle_len = (const int*)pCompressor->intervals /* reuse */; + salvador_arrival* cur_arrival; + int i; + + if ((nEndOffset - nStartOffset) > pCompressor->block_size) return; + + for (i = (nStartOffset * nMaxArrivalsPerPosition); i != ((nEndOffset + 1) * nMaxArrivalsPerPosition); i += nMaxArrivalsPerPosition) { + int j; + + memset(arrival + i, 0, sizeof(salvador_arrival) * nMaxArrivalsPerPosition); + + for (j = 0; j < nMaxArrivalsPerPosition; j++) + arrival[i + j].cost = 0x40000000; + } + + arrival[nStartOffset * nMaxArrivalsPerPosition].cost = 0; + arrival[nStartOffset * nMaxArrivalsPerPosition].from_slot = -1; + arrival[nStartOffset * nMaxArrivalsPerPosition].rep_offset = *nCurRepMatchOffset; + + if (nInsertForwardReps) { + salvador_visited* visited = ((salvador_visited*)pCompressor->pos_data) - nStartOffset /* reuse */; + + memset(visited + nStartOffset, 0, (nEndOffset - nStartOffset) * sizeof(salvador_visited)); + } + + for (i = nStartOffset, cur_arrival = &arrival[nStartOffset * nMaxArrivalsPerPosition]; i != nEndOffset; i++, cur_arrival += nMaxArrivalsPerPosition) { + salvador_arrival *pDestLiteralSlots = &cur_arrival[nMaxArrivalsPerPosition]; + int j, m; + + for (j = 0; j < nArrivalsPerPosition && cur_arrival[j].from_slot; j++) { + const int nNumLiterals = cur_arrival[j].num_literals + 1; + const int nCodingChoiceCost = cur_arrival[j].cost + 8 /* literal */ + (((nNumLiterals & (nNumLiterals - 1)) == 0) ? 2 : 0); + const int nScore = cur_arrival[j].score + 1; + const int nRepOffset = cur_arrival[j].rep_offset; + + if (nCodingChoiceCost < pDestLiteralSlots[nArrivalsPerPosition - 1].cost || + (nCodingChoiceCost == pDestLiteralSlots[nArrivalsPerPosition - 1].cost && + nScore < pDestLiteralSlots[nArrivalsPerPosition - 1].score && + nRepOffset != pDestLiteralSlots[nArrivalsPerPosition - 1].rep_offset)) { + int exists = 0, n; + + for (n = 0; + pDestLiteralSlots[n].cost < nCodingChoiceCost; + n++) { + if (pDestLiteralSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; + pDestLiteralSlots[n].cost == nCodingChoiceCost && nScore >= pDestLiteralSlots[n].score; + n++) { + if (pDestLiteralSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestLiteralSlots[z].cost == nCodingChoiceCost; z++) { + if (pDestLiteralSlots[z].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; z < nArrivalsPerPosition - 1 && pDestLiteralSlots[z].from_slot; z++) { + if (pDestLiteralSlots[z].rep_offset == nRepOffset) + break; + } + + memmove(&pDestLiteralSlots[n + 1], + &pDestLiteralSlots[n], + sizeof(salvador_arrival) * (z - n)); + + salvador_arrival* pDestArrival = &pDestLiteralSlots[n]; + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->rep_offset = nRepOffset; + pDestArrival->rep_pos = cur_arrival[j].rep_pos; + pDestArrival->match_len = 0; + pDestArrival->num_literals = nNumLiterals; + pDestArrival->score = nScore; + } + } + } + } + } + + if (i == nStartOffset && (nBlockFlags & 1)) continue; + + const int nNumArrivalsForThisPos = j; + int nOverallMinRepLen = 0, nOverallMaxRepLen = 0; + + int nRepMatchArrivalIdx[(2 * NMAX_ARRIVALS_PER_POSITION) + 1]; + int nNumRepMatchArrivals = 0; + + if (i < nEndOffset) { + unsigned int nMaxRepLenForPos = nEndOffset - i; + if (nMaxRepLenForPos > LCP_MAX) + nMaxRepLenForPos = LCP_MAX; + + const unsigned char* pInWindowStart = pInWindow + i; + const unsigned char* pInWindowMax = pInWindowStart + nMaxRepLenForPos; + + for (j = 0; j < nNumArrivalsForThisPos; j++) { + if (cur_arrival[j].num_literals) { + const int nRepOffset = cur_arrival[j].rep_offset; + + if (i >= nRepOffset) { + if (pInWindowStart[0] == pInWindowStart[-nRepOffset]) { + if (nRepOffset) { + const int nLen0 = rle_len[i - nRepOffset]; + const int nLen1 = rle_len[i]; + const int nMinLen = (nLen0 < nLen1) ? nLen0 : nLen1; + const unsigned char* pInWindowAtPos = pInWindowStart + nMinLen; + + if (pInWindowAtPos > pInWindowMax) + pInWindowAtPos = pInWindowMax; + + while ((pInWindowAtPos + 8) < pInWindowMax && !memcmp(pInWindowAtPos, pInWindowAtPos - nRepOffset, 8)) + pInWindowAtPos += 8; + while ((pInWindowAtPos + 4) < pInWindowMax && !memcmp(pInWindowAtPos, pInWindowAtPos - nRepOffset, 4)) + pInWindowAtPos += 4; + while (pInWindowAtPos < pInWindowMax && pInWindowAtPos[0] == pInWindowAtPos[-nRepOffset]) + pInWindowAtPos++; + const int nCurRepLen = (const int)(pInWindowAtPos - pInWindowStart); + + if (nOverallMaxRepLen < nCurRepLen) + nOverallMaxRepLen = nCurRepLen; + nRepMatchArrivalIdx[nNumRepMatchArrivals++] = j; + nRepMatchArrivalIdx[nNumRepMatchArrivals++] = nCurRepLen; + } + } + } + } + } + } + nRepMatchArrivalIdx[nNumRepMatchArrivals] = -1; + + const salvador_match* match = pCompressor->match + ((i - nStartOffset) * NMATCHES_PER_INDEX); + const unsigned short* match_depth = pCompressor->match_depth + ((i - nStartOffset) * NMATCHES_PER_INDEX); + + for (m = 0; m < NMATCHES_PER_INDEX && match[m].length; m++) { + int nOrigMatchLen = match[m].length; + const int nOrigMatchOffset = match[m].offset; + const unsigned int nOrigMatchDepth = match_depth[m]; + unsigned int d; + + if ((i + nOrigMatchLen) > nEndOffset) + nOrigMatchLen = nEndOffset - i; + + for (d = 0; d <= nOrigMatchDepth; d += (nOrigMatchDepth ? nOrigMatchDepth : 1)) { + const int nMatchLen = nOrigMatchLen - d; + const int nMatchOffset = nOrigMatchOffset - d; + int nNonRepMatchArrivalIdx, nStartingMatchLen, k; + + if (nInsertForwardReps) { + salvador_insert_forward_match(pCompressor, pInWindow, i, nMatchOffset, nStartOffset, nEndOffset, 0); + } + + nNonRepMatchArrivalIdx = -1; + for (j = 0; j < nNumArrivalsForThisPos; j++) { + const int nRepOffset = cur_arrival[j].rep_offset; + + if (nMatchOffset != nRepOffset || cur_arrival[j].num_literals == 0) { + nNonRepMatchArrivalIdx = j; + break; + } + } + + if (nNonRepMatchArrivalIdx >= 0) { + const int nNoRepmatchOffsetCost = cur_arrival[nNonRepMatchArrivalIdx].cost /* the actual cost of the literals themselves accumulates up the chain */ + OFFSET_COST(nMatchOffset); + const int nNoRepmatchScore = cur_arrival[nNonRepMatchArrivalIdx].score + 3; + + /* Insert non-repmatch candidate */ + + if (nMatchLen < LEAVE_ALONE_MATCH_SIZE) { + nStartingMatchLen = 2; + } + else { + nStartingMatchLen = nMatchLen; + } + + for (k = nStartingMatchLen; k <= nMatchLen; k++) { + salvador_arrival* pDestSlots = &cur_arrival[k * nMaxArrivalsPerPosition]; + const int nMatchLenCost = (k < 8192) ? salvador_cost_for_len[k - 1] : (salvador_get_match_varlen_size_norep(k) + TOKEN_SIZE /* token */); + const int nCodingChoiceCost = nMatchLenCost + nNoRepmatchOffsetCost; + + if (nCodingChoiceCost < pDestSlots[nArrivalsPerPosition - 2].cost || + (nCodingChoiceCost == pDestSlots[nArrivalsPerPosition - 2].cost && + nNoRepmatchScore < pDestSlots[nArrivalsPerPosition - 2].score && + (nCodingChoiceCost != pDestSlots[nArrivalsPerPosition - 1].cost || nMatchOffset != pDestSlots[nArrivalsPerPosition - 1].rep_offset))) { + int exists = 0, n; + + for (n = 0; + pDestSlots[n].cost < nCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nMatchOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; + pDestSlots[n].cost == nCodingChoiceCost && nNoRepmatchScore >= pDestSlots[n].score; + n++) { + if (pDestSlots[n].rep_offset == nMatchOffset) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestSlots[z].cost == nCodingChoiceCost; z++) { + if (pDestSlots[z].rep_offset == nMatchOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; z < nArrivalsPerPosition - 1 && pDestSlots[z].from_slot; z++) { + if (pDestSlots[z].rep_offset == nMatchOffset) + break; + } + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(salvador_arrival) * (z - n)); + + salvador_arrival* pDestArrival = &pDestSlots[n]; + pDestArrival->cost = nCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = nNonRepMatchArrivalIdx + 1; + pDestArrival->rep_offset = nMatchOffset; + pDestArrival->rep_pos = i; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nNoRepmatchScore; + } + } + } + } + } + } + + /* Insert repmatch candidates */ + + if (nMatchLen < LEAVE_ALONE_MATCH_SIZE || nMatchLen <= nOverallMinRepLen) { + nStartingMatchLen = nOverallMinRepLen + 1; + } + else { + nStartingMatchLen = nMatchLen; + } + + for (k = nStartingMatchLen; k <= (nOverallMaxRepLen < nMatchLen ? nOverallMaxRepLen : nMatchLen); k++) { + const int nMatchLenCost = (k < 8192) ? salvador_cost_for_len[k] : (salvador_get_match_varlen_size_rep(k) + TOKEN_SIZE /* token */); + salvador_arrival* pDestSlots = &cur_arrival[k * nMaxArrivalsPerPosition]; + int nCurRepMatchArrival; + + for (nCurRepMatchArrival = 0; (j = nRepMatchArrivalIdx[nCurRepMatchArrival]) >= 0; nCurRepMatchArrival += 2) { + if (nRepMatchArrivalIdx[nCurRepMatchArrival + 1] >= k) { + const int nRepCodingChoiceCost = cur_arrival[j].cost /* the actual cost of the literals themselves accumulates up the chain */ + nMatchLenCost; + const int nScore = cur_arrival[j].score + 2; + const int nRepOffset = cur_arrival[j].rep_offset; + + if (nRepCodingChoiceCost < pDestSlots[nArrivalsPerPosition - 1].cost || + (nRepCodingChoiceCost == pDestSlots[nArrivalsPerPosition - 1].cost && + nScore < pDestSlots[nArrivalsPerPosition - 1].score && + nRepOffset != pDestSlots[nArrivalsPerPosition - 1].rep_offset)) { + int exists = 0, n; + + for (n = 0; + pDestSlots[n].cost < nRepCodingChoiceCost; + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; + pDestSlots[n].cost == nRepCodingChoiceCost && nScore >= pDestSlots[n].score; + n++) { + if (pDestSlots[n].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + int z; + + for (z = n; z < nArrivalsPerPosition - 1 && pDestSlots[z].cost == nRepCodingChoiceCost; z++) { + if (pDestSlots[z].rep_offset == nRepOffset) { + exists = 1; + break; + } + } + + if (!exists) { + for (; z < nArrivalsPerPosition - 1 && pDestSlots[z].from_slot; z++) { + if (pDestSlots[z].rep_offset == nRepOffset) + break; + } + + memmove(&pDestSlots[n + 1], + &pDestSlots[n], + sizeof(salvador_arrival) * (z - n)); + + salvador_arrival* pDestArrival = &pDestSlots[n]; + pDestArrival->cost = nRepCodingChoiceCost; + pDestArrival->from_pos = i; + pDestArrival->from_slot = j + 1; + pDestArrival->rep_offset = nRepOffset; + pDestArrival->rep_pos = i; + pDestArrival->match_len = k; + pDestArrival->num_literals = 0; + pDestArrival->score = nScore; + } + } + } + } + else { + break; + } + } + } + + if (k <= LEAVE_ALONE_MATCH_SIZE) + nOverallMinRepLen = k; + else if (nOverallMaxRepLen == k) + nOverallMaxRepLen--; + } + } + + if (nOrigMatchLen >= 1280 && ((m + 1) >= NMATCHES_PER_INDEX || match[m + 1].length < 512)) + break; + } + } + + if (!nInsertForwardReps) { + const salvador_arrival* end_arrival = &arrival[i * nMaxArrivalsPerPosition]; + salvador_match* pBestMatch = pCompressor->best_match - nStartOffset; + + while (end_arrival->from_slot > 0 && end_arrival->from_pos < (const unsigned int)nEndOffset) { + pBestMatch[end_arrival->from_pos].length = end_arrival->match_len; + pBestMatch[end_arrival->from_pos].offset = (end_arrival->match_len) ? end_arrival->rep_offset : 0; + + end_arrival = &arrival[(end_arrival->from_pos * nMaxArrivalsPerPosition) + (end_arrival->from_slot - 1)]; + } + } +} + +/** + * Attempt to replace matches by literals when it makes the final bitstream smaller, and merge large matches + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param nCurRepMatchOffset starting rep offset for this block + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + * + * @return non-zero if the number of tokens was reduced, 0 if it wasn't + */ +static int salvador_reduce_commands(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nStartOffset, const int nEndOffset, const int *nCurRepMatchOffset, const int nBlockFlags) { + salvador_match* pBestMatch = pCompressor->best_match - nStartOffset; + int i; + int nNumLiterals = nBlockFlags & 1; + int nRepMatchOffset = *nCurRepMatchOffset; + int nDidReduce = 0; + + for (i = nStartOffset + (nBlockFlags & 1); i < nEndOffset; ) { + salvador_match *pMatch = pBestMatch + i; + + if (pMatch->length == 0 && + (i + 1) < nEndOffset && + pBestMatch[i + 1].length >= MIN_ENCODED_MATCH_SIZE && + pBestMatch[i + 1].length < MAX_VARLEN && + pBestMatch[i + 1].offset && + i >= pBestMatch[i + 1].offset && + (i + pBestMatch[i + 1].length + 1) <= nEndOffset && + (nNumLiterals != 0 || pBestMatch[i + 1].offset != nRepMatchOffset) && + !memcmp(pInWindow + i - (pBestMatch[i + 1].offset), pInWindow + i, pBestMatch[i + 1].length + 1)) { + int nCurLenSize, nReducedLenSize; + + if (pBestMatch[i + 1].offset == nRepMatchOffset) { + nCurLenSize = salvador_get_literals_varlen_size(nNumLiterals + 1) + 8 + salvador_get_match_varlen_size_rep(pBestMatch[i + 1].length); + } + else { + nCurLenSize = salvador_get_literals_varlen_size(nNumLiterals + 1) + 8 + + salvador_get_elias_size(((pBestMatch[i + 1].offset - 1) >> 7) + 1) + 7 + + salvador_get_match_varlen_size_norep(pBestMatch[i + 1].length); + } + + if (nNumLiterals != 0 && pBestMatch[i + 1].offset == nRepMatchOffset && nRepMatchOffset) { + nReducedLenSize = salvador_get_literals_varlen_size(nNumLiterals) + salvador_get_match_varlen_size_rep(pBestMatch[i + 1].length + 1); + } + else { + nReducedLenSize = salvador_get_literals_varlen_size(nNumLiterals) + + salvador_get_elias_size(((pBestMatch[i + 1].offset - 1) >> 7) + 1) + 7 + + salvador_get_match_varlen_size_norep(pBestMatch[i + 1].length + 1); + } + + if (nReducedLenSize <= nCurLenSize) { + /* Merge */ + pBestMatch[i].length = pBestMatch[i + 1].length + 1; + pBestMatch[i].offset = pBestMatch[i + 1].offset; + pBestMatch[i + 1].length = 0; + pBestMatch[i + 1].offset = 0; + nDidReduce = 1; + continue; + } + } + + if (pMatch->length >= MIN_ENCODED_MATCH_SIZE) { + if ((i + pMatch->length) < nEndOffset /* Don't consider the last match in the block, we can only reduce a match inbetween other tokens */) { + int nNextIndex = i + pMatch->length; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length == 0) { + nNextLiterals++; + nNextIndex++; + } + + if (nNextIndex < nEndOffset) { + /* This command is a match, is followed by 'nNextLiterals' literals and then by another match */ + + if (pBestMatch[nNextIndex].length >= MIN_ENCODED_MATCH_SIZE) { + if (nNumLiterals != 0 && nRepMatchOffset && pMatch->offset != nRepMatchOffset && (pBestMatch[nNextIndex].offset != pMatch->offset || + OFFSET_COST(pMatch->offset) > OFFSET_COST(pBestMatch[nNextIndex].offset))) { + /* Check if we can get a missed backward repmatch */ + if (i >= nRepMatchOffset && + (i - nRepMatchOffset + pMatch->length) <= nEndOffset) { + int nMaxLen = 0; + while ((nMaxLen + 8) < pMatch->length && !memcmp(pInWindow + i - nRepMatchOffset + nMaxLen, pInWindow + i - pMatch->offset + nMaxLen, 8)) + nMaxLen += 8; + while ((nMaxLen + 4) < pMatch->length && !memcmp(pInWindow + i - nRepMatchOffset + nMaxLen, pInWindow + i - pMatch->offset + nMaxLen, 4)) + nMaxLen += 4; + while (nMaxLen < pMatch->length && pInWindow[i - nRepMatchOffset + nMaxLen] == pInWindow[i - pMatch->offset + nMaxLen]) + nMaxLen++; + + if (nMaxLen >= 1) { + int nCurCommandSize, nReducedCommandSize; + + /* -- Original: Match with offset. Skip 'match with offset follows' bit. -- */ + + /* High bits of match offset */ + nCurCommandSize = salvador_get_elias_size(((pMatch->offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurCommandSize += 7; + + /* Match length */ + nCurCommandSize += salvador_get_match_varlen_size_norep(pMatch->length); + + /* Literals after command */ + nCurCommandSize += salvador_get_literals_varlen_size(nNextLiterals); + + /* -- Reduced: Rep match. Skip 'rep-match follows' bit. -- */ + + /* Match length */ + nReducedCommandSize = salvador_get_match_varlen_size_rep(nMaxLen); + + /* Literals after command */ + nReducedCommandSize += (pMatch->length - nMaxLen) << 3; + nReducedCommandSize += salvador_get_literals_varlen_size(nNextLiterals + (pMatch->length - nMaxLen)); + + if (nReducedCommandSize < nCurCommandSize) { + int j; + + /* Change to repmatch */ + + pMatch->offset = nRepMatchOffset; + for (j = nMaxLen; j < pMatch->length; j++) { + pBestMatch[i + j].length = 0; + } + pMatch->length = nMaxLen; + nDidReduce = 1; + } + } + } + } + + if (pBestMatch[nNextIndex].offset && pMatch->offset != pBestMatch[nNextIndex].offset && nRepMatchOffset != pBestMatch[nNextIndex].offset && nNextLiterals) { + /* Otherwise, try to gain a match forward as well */ + if (i >= pBestMatch[nNextIndex].offset && (i - pBestMatch[nNextIndex].offset + pMatch->length) <= nEndOffset && pMatch->offset != nRepMatchOffset) { + int nMaxLen = 0; + while ((nMaxLen + 8) < pMatch->length && !memcmp(pInWindow + i - pBestMatch[nNextIndex].offset + nMaxLen, pInWindow + i - pMatch->offset + nMaxLen, 8)) + nMaxLen += 8; + while ((nMaxLen + 4) < pMatch->length && !memcmp(pInWindow + i - pBestMatch[nNextIndex].offset + nMaxLen, pInWindow + i - pMatch->offset + nMaxLen, 4)) + nMaxLen += 4; + while (nMaxLen < pMatch->length && pInWindow[i - pBestMatch[nNextIndex].offset + nMaxLen] == pInWindow[i - pMatch->offset + nMaxLen]) + nMaxLen++; + if (nMaxLen >= pMatch->length) { + /* Replace */ + pMatch->offset = pBestMatch[nNextIndex].offset; + nDidReduce = 1; + } + else if (nMaxLen >= 2) { + int nPartialSizeBefore, nPartialSizeAfter; + + nPartialSizeBefore = salvador_get_match_varlen_size_norep(pMatch->length); + nPartialSizeBefore += OFFSET_COST(pMatch->offset); + nPartialSizeBefore += salvador_get_literals_varlen_size(nNextLiterals); + + nPartialSizeAfter = salvador_get_match_varlen_size_rep(nMaxLen); + nPartialSizeAfter += salvador_get_literals_varlen_size(nNextLiterals + (pMatch->length - nMaxLen)) + ((pMatch->length - nMaxLen) << 3); + + if (nPartialSizeAfter < nPartialSizeBefore) { + int j; + + /* We gain a repmatch that is shorter than the original match as this is the best we can do, so it is followed by extra literals, but + * we have calculated that this is shorter */ + pMatch->offset = pBestMatch[nNextIndex].offset; + for (j = nMaxLen; j < pMatch->length; j++) { + pBestMatch[i + j].length = 0; + } + pMatch->length = nMaxLen; + nDidReduce = 1; + } + } + } + } + } + + if (pMatch->length < 9 /* Don't waste time considering large matches, they will always win over literals */) { + /* Calculate this command's current cost */ + + int nCurCommandSize = salvador_get_literals_varlen_size(nNumLiterals); + /* Don't include current command's literal databits */ + + if (pMatch->offset == nRepMatchOffset && nNumLiterals != 0 && nRepMatchOffset) { + /* Rep match - don't include 'rep match follows' bit */ + + /* Match length */ + nCurCommandSize += salvador_get_match_varlen_size_rep(pMatch->length); + } + else { + /* Match with offset - don't include 'match with offset follows' bit */ + + /* High bits of match offset */ + nCurCommandSize += salvador_get_elias_size(((pMatch->offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurCommandSize += 7; + + /* Match length */ + nCurCommandSize += salvador_get_match_varlen_size_norep(pMatch->length); + } + + /* Calculate the next command's current cost */ + int nNextCommandSize = salvador_get_literals_varlen_size(nNextLiterals); + /* Don't include next command's literal databits */ + + /* Rep match or match with offset follows */ + nNextCommandSize += 1; + + if (pMatch->offset && pBestMatch[nNextIndex].offset == pMatch->offset && nNextLiterals != 0) { + /* Match length */ + nNextCommandSize += salvador_get_match_varlen_size_rep(pBestMatch[nNextIndex].length); + } + else { + /* High bits of match offset */ + nNextCommandSize += salvador_get_elias_size(((pBestMatch[nNextIndex].offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nNextCommandSize += 7; + + /* Match length */ + nNextCommandSize += salvador_get_match_varlen_size_norep(pBestMatch[nNextIndex].length); + } + + const int nOriginalCombinedCommandSize = nCurCommandSize + nNextCommandSize; + + /* Calculate the cost of replacing this match command by literals + the next command with the cost of encoding these literals */ + int nReducedCommandSize = (pMatch->length << 3); + nReducedCommandSize += salvador_get_literals_varlen_size(nNumLiterals + pMatch->length + nNextLiterals); + /* Don't include current + next command's literal databits */ + + if (pBestMatch[nNextIndex].offset == nRepMatchOffset && (nNumLiterals + pMatch->length + nNextLiterals) != 0 && nRepMatchOffset) { + /* Rep match - don't include 'rep match follows' bit */ + + /* Match length */ + nReducedCommandSize += salvador_get_match_varlen_size_rep(pBestMatch[nNextIndex].length); + } + else { + /* Match with offset - don't include 'match with offset follows' bit */ + + /* High bits of match offset */ + nReducedCommandSize += salvador_get_elias_size(((pBestMatch[nNextIndex].offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nReducedCommandSize += 7; + + /* Match length */ + nReducedCommandSize += salvador_get_match_varlen_size_norep(pBestMatch[nNextIndex].length); + } + + if (nOriginalCombinedCommandSize >= nReducedCommandSize) { + /* Reduce */ + const int nMatchLen = pMatch->length; + int j; + + for (j = 0; j < nMatchLen; j++) { + pBestMatch[i + j].length = 0; + } + + nDidReduce = 1; + continue; + } + } + } + } + + if ((i + pMatch->length) < nEndOffset && pMatch->offset && pMatch->length >= MIN_ENCODED_MATCH_SIZE && + pBestMatch[i + pMatch->length].offset && + pBestMatch[i + pMatch->length].length >= MIN_ENCODED_MATCH_SIZE && + (pMatch->length + pBestMatch[i + pMatch->length].length) <= MAX_VARLEN && + (i + pMatch->length) >= pMatch->offset && + (i + pMatch->length) >= pBestMatch[i + pMatch->length].offset && + (i + pMatch->length + pBestMatch[i + pMatch->length].length) <= nEndOffset && + !memcmp(pInWindow + i - pMatch->offset + pMatch->length, + pInWindow + i + pMatch->length - pBestMatch[i + pMatch->length].offset, + pBestMatch[i + pMatch->length].length)) { + + int nNextIndex = i + pMatch->length + pBestMatch[i + pMatch->length].length; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length == 0) { + nNextIndex++; + nNextLiterals++; + } + + int nCurPartialSize; + if (pMatch->offset == nRepMatchOffset && nNumLiterals != 0) { + /* Rep match. Don't include 'rep-match follows' bit. */ + + /* Match length */ + nCurPartialSize = salvador_get_match_varlen_size_rep(pMatch->length); + } + else { + /* Match with offset. Don't include 'match with offset follows' bit. */ + + /* High bits of match offset */ + nCurPartialSize = salvador_get_elias_size(((pMatch->offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurPartialSize += 7; + + /* Match length */ + nCurPartialSize += salvador_get_match_varlen_size_norep(pMatch->length); + } + + /* Match with offset */ + nCurPartialSize += 1; /* match with offset */ + + /* High bits of match offset */ + nCurPartialSize += salvador_get_elias_size(((pBestMatch[i + pMatch->length].offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurPartialSize += 7; + + /* Match length */ + nCurPartialSize += salvador_get_match_varlen_size_norep(pBestMatch[i + pMatch->length].length); + + if (nNextIndex < nEndOffset) { + if (pBestMatch[i + pMatch->length].offset && pBestMatch[nNextIndex].offset == pBestMatch[i + pMatch->length].offset && nNextLiterals != 0) { + /* Rep match. Don't include 'rep-match follows' bit. */ + + /* Match length */ + nCurPartialSize += salvador_get_match_varlen_size_rep(pBestMatch[nNextIndex].length); + } + else { + /* Match with offset. Don't include 'match with offset follows' bit. */ + + /* High bits of match offset */ + nCurPartialSize += salvador_get_elias_size(((pBestMatch[nNextIndex].offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurPartialSize += 7; + + /* Match length */ + nCurPartialSize += salvador_get_match_varlen_size_norep(pBestMatch[nNextIndex].length); + } + } + + int nReducedPartialSize; + if (pMatch->offset == nRepMatchOffset && nNumLiterals != 0 && nRepMatchOffset) { + /* Rep match. Don't include 'rep-match follows' bit. */ + + /* Match length */ + nReducedPartialSize = salvador_get_match_varlen_size_rep(pMatch->length + pBestMatch[i + pMatch->length].length); + } + else { + /* Match with offset. Don't include 'match with offset follows' bit. */ + + /* High bits of match offset */ + nReducedPartialSize = salvador_get_elias_size(((pMatch->offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nReducedPartialSize += 7; + + /* Match length */ + nReducedPartialSize += salvador_get_match_varlen_size_norep(pMatch->length + pBestMatch[i + pMatch->length].length); + } + + int nCannotReduce = 0; + if (nNextIndex < nEndOffset) { + if (pMatch->offset && pBestMatch[nNextIndex].offset == pMatch->offset && nNextLiterals != 0) { + /* Rep match. Don't include 'rep-match follows' bit. */ + + /* Match length */ + nReducedPartialSize += salvador_get_match_varlen_size_rep(pBestMatch[nNextIndex].length); + } + else { + if (pBestMatch[nNextIndex].length >= MIN_ENCODED_MATCH_SIZE) { + /* Match with offset. Don't include 'match with offset follows' bit. */ + + /* High bits of match offset */ + nReducedPartialSize += salvador_get_elias_size(((pBestMatch[nNextIndex].offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nReducedPartialSize += 7; + + /* Match length */ + nReducedPartialSize += salvador_get_match_varlen_size_norep(pBestMatch[nNextIndex].length); + } + else { + nCannotReduce = 1; + } + } + } + + if (nCurPartialSize >= nReducedPartialSize && !nCannotReduce) { + const int nMatchLen = pMatch->length; + + /* Join */ + + pMatch->length += pBestMatch[i + nMatchLen].length; + pBestMatch[i + nMatchLen].length = 0; + pBestMatch[i + nMatchLen].offset = 0; + nDidReduce = 1; + continue; + } + } + + if (nNumLiterals != 0 && pMatch->offset != nRepMatchOffset && pMatch->length == MIN_ENCODED_MATCH_SIZE && nRepMatchOffset) { + if ((i + MIN_ENCODED_MATCH_SIZE /* pMatch->length */) < nEndOffset) { + int nNextIndex = i + MIN_ENCODED_MATCH_SIZE /* pMatch->length */; + int nNextLiterals = 0; + + /* Check if we can turn a match + a 1 byte rep match into all literals, and either reduce the output or keep it the same size */ + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length == 0) { + nNextLiterals++; + nNextIndex++; + } + + if (nNextIndex < nEndOffset && nNextLiterals != 0 && + pBestMatch[nNextIndex].length == 1 && + pBestMatch[nNextIndex].offset == pMatch->offset) { + int nNextNextIndex = nNextIndex + 1 /* pBestMatch[nNextIndex].length */; + int nNextNextLiterals = 0; + + while (nNextNextIndex < nEndOffset && pBestMatch[nNextNextIndex].length == 0) { + nNextNextLiterals++; + nNextNextIndex++; + } + + if (nNextNextIndex < nEndOffset && nNextNextLiterals != 0 && + pBestMatch[nNextNextIndex].length >= MIN_ENCODED_MATCH_SIZE && + pBestMatch[nNextNextIndex].offset != pBestMatch[nNextIndex].offset) { + int nCurCommandSize, nCurRepMatchSize, nReducedCommandSize; + + /* First command: match with offset */ + nCurCommandSize = salvador_get_literals_varlen_size(nNumLiterals); + /* Don't include match with offset's literal databits */ + + /* Match with offset */ + nCurCommandSize += 1; /* match with offset follows */ + + /* High bits of match offset */ + nCurCommandSize += salvador_get_elias_size(((pMatch->offset - 1) >> 7) + 1); + + /* Low byte of match offset */ + nCurCommandSize += 7; + + /* Match length */ + nCurCommandSize += 1 /* salvador_get_match_varlen_size_norep(pMatch->length) */; + + /* Second command: rep-match */ + nCurRepMatchSize = salvador_get_literals_varlen_size(nNextLiterals); + nCurRepMatchSize += (nNextLiterals << 3); + + nCurRepMatchSize += 1; /* rep-match follows */ + nCurRepMatchSize += 1 /* salvador_get_match_varlen_size_rep(pBestMatch[nNextIndex].length) */; + + /* Combined commands as literals */ + nReducedCommandSize = salvador_get_literals_varlen_size(nNumLiterals + MIN_ENCODED_MATCH_SIZE /* pMatch->length */ + nNextLiterals + 1 /* pBestMatch[nNextIndex].length */); + /* Don't include combined command's literal databits */ + nReducedCommandSize += (MIN_ENCODED_MATCH_SIZE /* pMatch->length */ << 3); + nReducedCommandSize += (nNextLiterals << 3); + nReducedCommandSize += (1 /* pBestMatch[nNextIndex].length */ << 3); + + if ((nCurCommandSize + nCurRepMatchSize) >= nReducedCommandSize) { + int j; + + for (j = 0; j < MIN_ENCODED_MATCH_SIZE /* pMatch->length */; j++) { + pBestMatch[i + j].length = 0; + } + + pBestMatch[nNextIndex].length = 0; + nDidReduce = 1; + } + } + } + } + } + + nRepMatchOffset = pMatch->offset; + + i += pMatch->length; + nNumLiterals = 0; + } + else if (pMatch->length == 1) { + if (nNumLiterals != 0) { + int nNextIndex = i + 1 /* pMatch->length */; + int nNextLiterals = 0; + + while (nNextIndex < nEndOffset && pBestMatch[nNextIndex].length == 0) { + nNextLiterals++; + nNextIndex++; + } + + if (nRepMatchOffset != pMatch->offset && (nNextIndex < nEndOffset || !nDidReduce)) { + pMatch->length = 0; + pMatch->offset = 0; + nDidReduce = 1; + continue; + } + + if (nNextLiterals != 0) { + int nCurPartialSize = salvador_get_literals_varlen_size(nNumLiterals); + nCurPartialSize += TOKEN_SIZE + 1 /* salvador_get_match_varlen_size_rep(pMatch->length) */; + nCurPartialSize += salvador_get_literals_varlen_size(nNextLiterals); + + const int nReducedPartialSize = salvador_get_literals_varlen_size(nNumLiterals + 1 + nNextLiterals) + 8; + + if (nCurPartialSize >= nReducedPartialSize) { + pMatch->length = 0; + pMatch->offset = 0; + nDidReduce = 1; + continue; + } + } + } + + nNumLiterals = 0; + i++; + } + else { + nNumLiterals++; + i++; + } + } + + return nDidReduce; +} + +/** + * Emit a block of compressed data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nStartOffset current offset in input window (typically the number of previously compressed bytes) + * @param nEndOffset offset to end finding matches at (typically the size of the total input window in bytes + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * @param nFinalLiterals output number of literals not written after writing this block, that need to be written in the next block + * @param nCurRepMatchOffset starting rep offset for this block, updated after the block is compressed successfully + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int salvador_write_block(salvador_compressor* pCompressor, const unsigned char* pInWindow, const int nStartOffset, const int nEndOffset, unsigned char* pOutData, const int nMaxOutDataSize, int* nCurBitsOffset, int* nCurBitShift, int* nFinalLiterals, int* nCurRepMatchOffset, const int nBlockFlags) { + const salvador_match* pBestMatch = pCompressor->best_match - nStartOffset; + int nRepMatchOffset = *nCurRepMatchOffset; + int nOutOffset = 0; + const int nMaxOffset = pCompressor->max_offset; + const int nIsInverted = pCompressor->flags & FLG_IS_INVERTED; + const int nIsBackward = (pCompressor->flags & FLG_IS_BACKWARD) >> 1; + int nNumLiterals = 0; + int nInFirstLiteralOffset = 0; + int nIsFirstCommand = nBlockFlags & 1; + int i; + + for (i = nStartOffset; i < nEndOffset; ) { + const salvador_match* pMatch = pBestMatch + i; + + if (pMatch->length >= 2 || (pMatch->length == 1 && pMatch->offset == nRepMatchOffset && nNumLiterals != 0)) { + const int nMatchLen = pMatch->length; + const int nMatchOffset = pMatch->offset; + + if (nMatchOffset < MIN_OFFSET || nMatchOffset > nMaxOffset || nMatchOffset > MAX_OFFSET) + return -1; + + if (nIsFirstCommand && nNumLiterals == 0) { + /* The first block always starts with a literal */ + return -1; + } + + if (nNumLiterals != 0) { + /* Literals */ + + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + if (!nIsFirstCommand) { + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, 0 /* literals follow */, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + } + + /* The command code for subsequent literals isn't omitted */ + nIsFirstCommand = 0; + + nOutOffset = salvador_write_normal_elias_value(pOutData, nOutOffset, nMaxOutDataSize, nNumLiterals, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + if ((nOutOffset + nNumLiterals) > nMaxOutDataSize) + return -1; + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + } + + if (nMatchOffset == nRepMatchOffset && nNumLiterals != 0) { + /* Rep match */ + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, 0 /* rep match */, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + /* Write match length */ + nOutOffset = salvador_write_normal_elias_value(pOutData, nOutOffset, nMaxOutDataSize, nMatchLen, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + } + else { + /* Match with offset */ + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, 1 /* match with offset */, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + /* Write high bits of match offset */ + if (nIsInverted) + nOutOffset = salvador_write_inverted_elias_value(pOutData, nOutOffset, nMaxOutDataSize, ((nMatchOffset - 1) >> 7) + 1, nIsBackward, nCurBitsOffset, nCurBitShift); + else + nOutOffset = salvador_write_normal_elias_value(pOutData, nOutOffset, nMaxOutDataSize, ((nMatchOffset - 1) >> 7) + 1, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + /* Write low byte of match offset */ + if (nOutOffset >= nMaxOutDataSize) + return -1; + if (nIsBackward) + pOutData[nOutOffset++] = ((nMatchOffset - 1) & 0x7f) << 1; + else + pOutData[nOutOffset++] = (255 - ((nMatchOffset - 1) & 0x7f)) << 1; + + /* Write match length */ + if (nMatchLen > 2) { + if (nIsBackward) + pOutData[nOutOffset - 1] |= 1; + nOutOffset = salvador_write_split_elias_value(pOutData, nOutOffset, nMaxOutDataSize, nMatchLen - 1, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + } + else { + if (!nIsBackward) + pOutData[nOutOffset - 1] |= 1; + } + } + + nNumLiterals = 0; + + if (nMatchOffset == nRepMatchOffset) + pCompressor->stats.num_rep_matches++; + else + pCompressor->stats.num_normal_matches++; + + nRepMatchOffset = nMatchOffset; + + if (nMatchOffset < pCompressor->stats.min_offset || pCompressor->stats.min_offset == -1) + pCompressor->stats.min_offset = nMatchOffset; + if (nMatchOffset > pCompressor->stats.max_offset) + pCompressor->stats.max_offset = nMatchOffset; + pCompressor->stats.total_offsets += (long long)nMatchOffset; + + if (nMatchLen < pCompressor->stats.min_match_len || pCompressor->stats.min_match_len == -1) + pCompressor->stats.min_match_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_match_len) + pCompressor->stats.max_match_len = nMatchLen; + pCompressor->stats.total_match_lens += nMatchLen; + pCompressor->stats.match_divisor++; + + if (nMatchOffset == 1) { + if (nMatchLen < pCompressor->stats.min_rle1_len || pCompressor->stats.min_rle1_len == -1) + pCompressor->stats.min_rle1_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle1_len) + pCompressor->stats.max_rle1_len = nMatchLen; + pCompressor->stats.total_rle1_lens += nMatchLen; + pCompressor->stats.rle1_divisor++; + } + else if (nMatchOffset == 2) { + if (nMatchLen < pCompressor->stats.min_rle2_len || pCompressor->stats.min_rle2_len == -1) + pCompressor->stats.min_rle2_len = nMatchLen; + if (nMatchLen > pCompressor->stats.max_rle2_len) + pCompressor->stats.max_rle2_len = nMatchLen; + pCompressor->stats.total_rle2_lens += nMatchLen; + pCompressor->stats.rle2_divisor++; + } + + i += nMatchLen; + + const int nCurSafeDist = (i - nStartOffset) - nOutOffset; + if (nCurSafeDist >= 0 && pCompressor->stats.safe_dist < nCurSafeDist) + pCompressor->stats.safe_dist = nCurSafeDist; + + pCompressor->stats.commands_divisor++; + } + else { + if (nNumLiterals == 0) + nInFirstLiteralOffset = i; + nNumLiterals++; + i++; + } + } + + if (nBlockFlags & 2) { + if (nNumLiterals < pCompressor->stats.min_literals || pCompressor->stats.min_literals == -1) + pCompressor->stats.min_literals = nNumLiterals; + if (nNumLiterals > pCompressor->stats.max_literals) + pCompressor->stats.max_literals = nNumLiterals; + pCompressor->stats.total_literals += nNumLiterals; + pCompressor->stats.literals_divisor++; + + *nFinalLiterals = 0; + + if (nNumLiterals != 0) { + /* Final Literals */ + + if (!nIsFirstCommand) { + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, 0 /* literals follow */, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + } + + nOutOffset = salvador_write_normal_elias_value(pOutData, nOutOffset, nMaxOutDataSize, nNumLiterals, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + if ((nOutOffset + nNumLiterals) > nMaxOutDataSize) + return -1; + memcpy(pOutData + nOutOffset, pInWindow + nInFirstLiteralOffset, nNumLiterals); + nOutOffset += nNumLiterals; + } + + nOutOffset = salvador_write_data_bit(pOutData, nOutOffset, nMaxOutDataSize, 1 /* match with offset */, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + if (nIsInverted) + nOutOffset = salvador_write_inverted_elias_value(pOutData, nOutOffset, nMaxOutDataSize, 256 /* EOD */, nIsBackward, nCurBitsOffset, nCurBitShift); + else + nOutOffset = salvador_write_normal_elias_value(pOutData, nOutOffset, nMaxOutDataSize, 256 /* EOD */, nIsBackward, nCurBitsOffset, nCurBitShift); + if (nOutOffset < 0) return -1; + + pCompressor->stats.num_eod++; + } + else { + *nFinalLiterals = nNumLiterals; + } + + *nCurRepMatchOffset = nRepMatchOffset; + return nOutOffset; +} + +/** + * Select the most optimal matches, reduce the token count if possible, and then emit a block of compressed data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * @param nFinalLiterals output number of literals not written after writing this block, that need to be written in the next block + * @param nCurRepMatchOffset starting rep offset for this block, updated after the block is compressed successfully + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int salvador_optimize_and_write_block(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize, int *nCurBitsOffset, int *nCurBitShift, int *nFinalLiterals, int *nCurRepMatchOffset, const int nBlockFlags) { + const int nEndOffset = nPreviousBlockSize + nInDataSize; + int *rle_len = (int*)pCompressor->intervals /* reuse */; + int *first_offset_for_byte = pCompressor->first_offset_for_byte; + int *next_offset_for_pos = pCompressor->next_offset_for_pos; + int *offset_cache = pCompressor->offset_cache; + int i, nPosition; + + memset(pCompressor->best_match, 0, pCompressor->block_size * sizeof(salvador_match)); + + /* Count identical bytes */ + + i = 0; + while (i < nEndOffset) { + int nRangeStartIdx = i; + const unsigned char c = pInWindow[nRangeStartIdx]; + + do { + i++; + } while (i < nEndOffset && pInWindow[i] == c); + + while (nRangeStartIdx < i) { + rle_len[nRangeStartIdx] = i - nRangeStartIdx; + nRangeStartIdx++; + } + } + + /* Supplement small matches */ + + memset(first_offset_for_byte, 0xff, sizeof(int) * 65536); + memset(next_offset_for_pos, 0xff, sizeof(int) * nInDataSize); + + for (nPosition = nPreviousBlockSize; nPosition < (nEndOffset - 1); nPosition++) { + next_offset_for_pos[nPosition - nPreviousBlockSize] = first_offset_for_byte[((unsigned int)pInWindow[nPosition]) | (((unsigned int)pInWindow[nPosition + 1]) << 8)]; + first_offset_for_byte[((unsigned int)pInWindow[nPosition]) | (((unsigned int)pInWindow[nPosition + 1]) << 8)] = nPosition; + } + + memset(offset_cache, 0xff, sizeof(int) * 2048); + + for (nPosition = nPreviousBlockSize + 1; nPosition < (nEndOffset - 1); nPosition++) { + salvador_match *match = pCompressor->match + ((nPosition - nPreviousBlockSize) * NMATCHES_PER_INDEX); + const int nMaxMatchLen = ((nPosition + 130) < nEndOffset) ? 130 : (nEndOffset - nPosition); + const unsigned char* pInWindowMax = pInWindow + nPosition + nMaxMatchLen; + const unsigned char* pInWindowStart = pInWindow + nPosition; + unsigned short *match_depth = pCompressor->match_depth + ((nPosition - nPreviousBlockSize) * NMATCHES_PER_INDEX); + int m = 0; + int nMatchPos; + + while (m < 16 && match[m].length) { + offset_cache[match[m].offset & 2047] = nPosition; + offset_cache[(match[m].offset - match_depth[m]) & 2047] = nPosition; + m++; + } + + for (nMatchPos = next_offset_for_pos[nPosition - nPreviousBlockSize]; m < 16 && nMatchPos >= 0; nMatchPos = next_offset_for_pos[nMatchPos - nPreviousBlockSize]) { + const int nMatchOffset = nPosition - nMatchPos; + + if (nMatchOffset <= pCompressor->max_offset) { + int nAlreadyExists = 0; + + if (offset_cache[nMatchOffset & 2047] == nPosition) { + int nExistingMatchIdx; + + for (nExistingMatchIdx = 0; nExistingMatchIdx < m; nExistingMatchIdx++) { + if (match[nExistingMatchIdx].offset == nMatchOffset || + (match[nExistingMatchIdx].offset - match_depth[nExistingMatchIdx]) == nMatchOffset) { + nAlreadyExists = 1; + break; + } + } + } + + if (!nAlreadyExists) { + const int nLen0 = rle_len[nMatchPos]; + const int nLen1 = rle_len[nPosition]; + const int nMinLen = (nLen0 < nLen1) ? nLen0 : nLen1; + const unsigned char* pInWindowAtPos = pInWindowStart + nMinLen; + + if (pInWindowAtPos > pInWindowMax) + pInWindowAtPos = pInWindowMax; + + while ((pInWindowAtPos + 8) < pInWindowMax && !memcmp(pInWindowAtPos, pInWindowAtPos - nMatchOffset, 8)) + pInWindowAtPos += 8; + while ((pInWindowAtPos + 4) < pInWindowMax && !memcmp(pInWindowAtPos, pInWindowAtPos - nMatchOffset, 4)) + pInWindowAtPos += 4; + while (pInWindowAtPos < pInWindowMax && pInWindowAtPos[0] == pInWindowAtPos[-nMatchOffset]) + pInWindowAtPos++; + + match[m].length = (const unsigned short)(pInWindowAtPos - pInWindowStart); + match[m].offset = nMatchOffset; + match_depth[m] = 0; + m++; + } + } + else { + break; + } + } + } + + /* Compress and insert additional matches */ + salvador_optimize_forward(pCompressor, pInWindow, nPreviousBlockSize, nEndOffset, 1 /* nInsertForwardReps */, nCurRepMatchOffset, NINITIAL_ARRIVALS_PER_POSITION, nBlockFlags); + + /* Supplement matches further */ + + for (nPosition = nPreviousBlockSize + 1; nPosition < (nEndOffset - 1); nPosition++) { + salvador_match* match = pCompressor->match + ((nPosition - nPreviousBlockSize) * NMATCHES_PER_INDEX); + + if (match[0].length < 8) { + const int nMaxMatchLen = ((nPosition + 130) < nEndOffset) ? 130 : (nEndOffset - nPosition); + const unsigned char* pInWindowMax = pInWindow + nPosition + nMaxMatchLen; + const unsigned char* pInWindowStart = pInWindow + nPosition; + unsigned short* match_depth = pCompressor->match_depth + ((nPosition - nPreviousBlockSize) * NMATCHES_PER_INDEX); + int m = 0, nInserted = 0; + int nMatchPos; + int nMaxForwardPos = nPosition + 2 + 1 + 3; + + if (nMaxForwardPos > (nEndOffset - 2)) + nMaxForwardPos = nEndOffset - 2; + + while (m < NMATCHES_PER_INDEX && match[m].length) { + offset_cache[match[m].offset & 2047] = nPosition; + offset_cache[(match[m].offset - match_depth[m]) & 2047] = nPosition; + m++; + } + + for (nMatchPos = next_offset_for_pos[nPosition - nPreviousBlockSize]; m < NMATCHES_PER_INDEX && nMatchPos >= 0; nMatchPos = next_offset_for_pos[nMatchPos - nPreviousBlockSize]) { + const int nMatchOffset = nPosition - nMatchPos; + + if (nMatchOffset <= pCompressor->max_offset) { + int nAlreadyExists = 0; + + if (offset_cache[nMatchOffset & 2047] == nPosition) { + int nExistingMatchIdx; + + for (nExistingMatchIdx = 0; nExistingMatchIdx < m; nExistingMatchIdx++) { + if (match[nExistingMatchIdx].offset == nMatchOffset || + (match[nExistingMatchIdx].offset - match_depth[nExistingMatchIdx]) == nMatchOffset) { + nAlreadyExists = 1; + break; + } + } + } + + if (!nAlreadyExists) { + int nForwardPos = nPosition + 2 + 1; + + if (nForwardPos >= nMatchOffset) { + while (nForwardPos < nMaxForwardPos) { + if (pInWindow[nForwardPos] == pInWindow[nForwardPos - nMatchOffset]) { + break; + } + nForwardPos++; + } + + if (nForwardPos < nMaxForwardPos) { + const int nLen0 = rle_len[nMatchPos]; + const int nLen1 = rle_len[nPosition]; + const int nMinLen = (nLen0 < nLen1) ? nLen0 : nLen1; + const unsigned char* pInWindowAtPos = pInWindowStart + nMinLen; + + if (pInWindowAtPos > pInWindowMax) + pInWindowAtPos = pInWindowMax; + + while ((pInWindowAtPos + 8) < pInWindowMax && !memcmp(pInWindowAtPos, pInWindowAtPos - nMatchOffset, 8)) + pInWindowAtPos += 8; + while ((pInWindowAtPos + 4) < pInWindowMax && !memcmp(pInWindowAtPos, pInWindowAtPos - nMatchOffset, 4)) + pInWindowAtPos += 4; + while (pInWindowAtPos < pInWindowMax && pInWindowAtPos[0] == pInWindowAtPos[-nMatchOffset]) + pInWindowAtPos++; + + match[m].length = (const unsigned short)(pInWindowAtPos - pInWindowStart); + match[m].offset = nMatchOffset; + match_depth[m] = 0; + m++; + + salvador_insert_forward_match(pCompressor, pInWindow, nPosition, nMatchOffset, nPreviousBlockSize, nEndOffset, 8); + + nInserted++; + if (nInserted >= 10) + break; + } + } + } + } + else { + break; + } + } + } + } + + /* Pick final matches */ + salvador_optimize_forward(pCompressor, pInWindow, nPreviousBlockSize, nEndOffset, 0 /* nInsertForwardReps */, nCurRepMatchOffset, pCompressor->max_arrivals_per_position, nBlockFlags); + + /* Apply reduction and merge pass */ + int nDidReduce; + int nPasses = 0; + do { + nDidReduce = salvador_reduce_commands(pCompressor, pInWindow, nPreviousBlockSize, nEndOffset, nCurRepMatchOffset, nBlockFlags); + nPasses++; + } while (nDidReduce && nPasses < 20); + + /* Write compressed block */ + + return salvador_write_block(pCompressor, pInWindow, nPreviousBlockSize, nEndOffset, pOutData, nMaxOutDataSize, nCurBitsOffset, nCurBitShift, nFinalLiterals, nCurRepMatchOffset, nBlockFlags); +} + +/* Forward declaration */ +static void salvador_compressor_destroy(salvador_compressor *pCompressor); + +/** + * Initialize compression context + * + * @param pCompressor compression context to initialize + * @param nBlockSize maximum size of input data (bytes to compress only) + * @param nMaxWindowSize maximum size of input data window (previously compressed bytes + bytes to compress) + * @param nMaxOffset maximum match offset to use (0 for default) + * @param nMaxArrivals maximum number of arrivals per position + * @param nFlags compression flags + * + * @return 0 for success, non-zero for failure + */ +static int salvador_compressor_init(salvador_compressor *pCompressor, const int nBlockSize, const int nMaxWindowSize, const size_t nMaxOffset, const int nMaxArrivals, const int nFlags) { + int nResult; + + nResult = divsufsort_init(&pCompressor->divsufsort_context); + pCompressor->intervals = NULL; + pCompressor->pos_data = NULL; + pCompressor->open_intervals = NULL; + pCompressor->match = NULL; + pCompressor->match_depth = NULL; + pCompressor->best_match = NULL; + pCompressor->arrival = NULL; + pCompressor->first_offset_for_byte = NULL; + pCompressor->next_offset_for_pos = NULL; + pCompressor->offset_cache = NULL; + if (nFlags & FLG_IS_BACKWARD) + pCompressor->flags = nFlags & (~FLG_IS_INVERTED); + else + pCompressor->flags = nFlags; + pCompressor->block_size = nBlockSize; + pCompressor->max_offset = nMaxOffset ? (int)nMaxOffset : MAX_OFFSET; + pCompressor->max_arrivals_per_position = nMaxArrivals; + + memset(&pCompressor->stats, 0, sizeof(pCompressor->stats)); + pCompressor->stats.min_match_len = -1; + pCompressor->stats.min_offset = -1; + pCompressor->stats.min_rle1_len = -1; + pCompressor->stats.min_rle2_len = -1; + + if (!nResult) { + pCompressor->intervals = (unsigned long long *)malloc(nMaxWindowSize * sizeof(unsigned long long)); + + if (pCompressor->intervals) { + pCompressor->pos_data = (unsigned long long *)malloc(nMaxWindowSize * sizeof(unsigned long long)); + + if (pCompressor->pos_data) { + pCompressor->open_intervals = (unsigned long long *)malloc((LCP_AND_TAG_MAX + 1) * sizeof(unsigned long long)); + + if (pCompressor->open_intervals) { + pCompressor->arrival = (salvador_arrival *)malloc((nBlockSize + 1) * nMaxArrivals * sizeof(salvador_arrival)); + + if (pCompressor->arrival) { + pCompressor->best_match = (salvador_match *)malloc(nBlockSize * sizeof(salvador_match)); + + if (pCompressor->best_match) { + pCompressor->match = (salvador_match *)malloc(nBlockSize * NMATCHES_PER_INDEX * sizeof(salvador_match)); + if (pCompressor->match) { + pCompressor->match_depth = (unsigned short *)malloc(nBlockSize * NMATCHES_PER_INDEX * sizeof(unsigned short)); + if (pCompressor->match_depth) { + pCompressor->first_offset_for_byte = (int*)malloc(65536 * sizeof(int)); + if (pCompressor->first_offset_for_byte) { + pCompressor->next_offset_for_pos = (int*)malloc(nBlockSize * sizeof(int)); + if (pCompressor->next_offset_for_pos) { + pCompressor->offset_cache = (int*)malloc(2048 * sizeof(int)); + if (pCompressor->offset_cache) { + return 0; + } + } + } + } + } + } + } + } + } + } + } + + salvador_compressor_destroy(pCompressor); + return 100; +} + +/** + * Clean up compression context and free up any associated resources + * + * @param pCompressor compression context to clean up + */ +static void salvador_compressor_destroy(salvador_compressor *pCompressor) { + divsufsort_destroy(&pCompressor->divsufsort_context); + + if (pCompressor->offset_cache) { + free(pCompressor->offset_cache); + pCompressor->offset_cache = NULL; + } + + if (pCompressor->next_offset_for_pos) { + free(pCompressor->next_offset_for_pos); + pCompressor->next_offset_for_pos = NULL; + } + + if (pCompressor->first_offset_for_byte) { + free(pCompressor->first_offset_for_byte); + pCompressor->first_offset_for_byte = NULL; + } + + if (pCompressor->match_depth) { + free(pCompressor->match_depth); + pCompressor->match_depth = NULL; + } + + if (pCompressor->match) { + free(pCompressor->match); + pCompressor->match = NULL; + } + + if (pCompressor->arrival) { + free(pCompressor->arrival); + pCompressor->arrival = NULL; + } + + if (pCompressor->best_match) { + free(pCompressor->best_match); + pCompressor->best_match = NULL; + } + + if (pCompressor->open_intervals) { + free(pCompressor->open_intervals); + pCompressor->open_intervals = NULL; + } + + if (pCompressor->pos_data) { + free(pCompressor->pos_data); + pCompressor->pos_data = NULL; + } + + if (pCompressor->intervals) { + free(pCompressor->intervals); + pCompressor->intervals = NULL; + } +} + +/** + * Compress one block of data + * + * @param pCompressor compression context + * @param pInWindow pointer to input data window (previously compressed bytes + bytes to compress) + * @param nPreviousBlockSize number of previously compressed bytes (or 0 for none) + * @param nInDataSize number of input bytes to compress + * @param pOutData pointer to output buffer + * @param nMaxOutDataSize maximum size of output buffer, in bytes + * @param nCurBitsOffset write index into output buffer, of current byte being filled with bits + * @param nCurBitShift bit shift count + * @param nFinalLiterals output number of literals not written after writing this block, that need to be written in the next block + * @param nCurRepMatchOffset starting rep offset for this block, updated after the block is compressed successfully + * @param nBlockFlags bit 0: 1 for first block, 0 otherwise; bit 1: 1 for last block, 0 otherwise + * + * @return size of compressed data in output buffer, or -1 if the data is uncompressible + */ +static int salvador_compressor_shrink_block(salvador_compressor *pCompressor, const unsigned char *pInWindow, const int nPreviousBlockSize, const int nInDataSize, unsigned char *pOutData, const int nMaxOutDataSize, int *nCurBitsOffset, int *nCurBitShift, int *nFinalLiterals, int *nCurRepMatchOffset, const int nBlockFlags) { + int nCompressedSize; + + if (salvador_build_suffix_array(pCompressor, pInWindow, nPreviousBlockSize + nInDataSize)) + nCompressedSize = -1; + else { + if (nPreviousBlockSize) { + salvador_skip_matches(pCompressor, 0, nPreviousBlockSize); + } + salvador_find_all_matches(pCompressor, NMATCHES_PER_INDEX, nPreviousBlockSize, nPreviousBlockSize + nInDataSize); + + nCompressedSize = salvador_optimize_and_write_block(pCompressor, pInWindow, nPreviousBlockSize, nInDataSize, pOutData, nMaxOutDataSize, nCurBitsOffset, nCurBitShift, nFinalLiterals, nCurRepMatchOffset, nBlockFlags); + } + + return nCompressedSize; +} + +/** + * Get maximum compressed size of input(source) data + * + * @param nInputSize input(source) size in bytes + * + * @return maximum compressed size + */ +size_t salvador_get_max_compressed_size(const size_t nInputSize) { + return ((nInputSize + 65535) >> 16) * 128 + nInputSize; +} + +/** + * Compress memory + * + * @param pInputData pointer to input(source) data to compress + * @param pOutBuffer buffer for compressed data + * @param nInputSize input(source) size in bytes + * @param nMaxOutBufferSize maximum capacity of compression buffer + * @param nFlags compression flags (set to FLG_IS_INVERTED) + * @param nMaxOffset maximum match offset to use (0 for default) + * @param nDictionarySize size of dictionary in front of input data (0 for none) + * @param progress progress function, called after compressing each block, or NULL for none + * @param pStats pointer to compression stats that are filled if this function is successful, or NULL + * + * @return actual compressed size, or -1 for error + */ +size_t salvador_compress(const unsigned char *pInputData, unsigned char *pOutBuffer, const size_t nInputSize, const size_t nMaxOutBufferSize, + const unsigned int nFlags, const size_t nMaxOffset, const size_t nDictionarySize, void(*progress)(long long nOriginalSize, long long nCompressedSize), salvador_stats *pStats) { + salvador_compressor compressor; + size_t nOriginalSize = 0; + size_t nCompressedSize = 0L; + int nResult; + int nMaxArrivals = NMAX_ARRIVALS_PER_POSITION; + int nError = 0; + const int nBlockSize = (nInputSize < BLOCK_SIZE) ? ((nInputSize < 1024) ? 1024 : (int)nInputSize) : BLOCK_SIZE; + const int nMaxOutBlockSize = (int)salvador_get_max_compressed_size(nBlockSize); + + nResult = salvador_compressor_init(&compressor, nBlockSize, nBlockSize * 2, nMaxOffset, nMaxArrivals, nFlags); + if (nResult != 0) { + return -1; + } + + int nPreviousBlockSize = 0; + int nNumBlocks = 0; + int nCurBitsOffset = 0, nCurBitShift = -1, nCurFinalLiterals = 0; + int nBlockFlags = 1; + int nCurRepMatchOffset = 1; + + if (nDictionarySize) { + nOriginalSize = (int)nDictionarySize; + nPreviousBlockSize = (int)nDictionarySize; + } + + while (nOriginalSize < nInputSize && !nError) { + int nInDataSize; + + nInDataSize = (int)(nInputSize - nOriginalSize); + if (nInDataSize > nBlockSize) + nInDataSize = nBlockSize; + + if (nInDataSize > 0) { + int nOutDataSize; + int nOutDataEnd = (int)(nMaxOutBufferSize - nCompressedSize); + + if (nOutDataEnd > nMaxOutBlockSize) + nOutDataEnd = nMaxOutBlockSize; + + if ((nOriginalSize + nInDataSize) >= nInputSize) + nBlockFlags |= 2; + nOutDataSize = salvador_compressor_shrink_block(&compressor, pInputData + nOriginalSize - nPreviousBlockSize, nPreviousBlockSize, nInDataSize, pOutBuffer + nCompressedSize, nOutDataEnd, + &nCurBitsOffset, &nCurBitShift, &nCurFinalLiterals, &nCurRepMatchOffset, nBlockFlags); + nBlockFlags &= (~1); + + if (nOutDataSize >= 0 && nCurFinalLiterals >= 0 && nCurFinalLiterals < nInDataSize) { + /* Write compressed block */ + + nInDataSize -= nCurFinalLiterals; + nOriginalSize += nInDataSize; + nCurFinalLiterals = 0; + nCompressedSize += nOutDataSize; + if (nCurBitShift != -1) + nCurBitsOffset -= nOutDataSize; + } + else { + nError = -1; + } + + nPreviousBlockSize = nInDataSize; + nNumBlocks++; + } + + if (!nError && nOriginalSize < nInputSize) { + if (progress) + progress(nOriginalSize, nCompressedSize); + } + } + + if (progress) + progress(nOriginalSize, nCompressedSize); + if (pStats) + *pStats = compressor.stats; + + salvador_compressor_destroy(&compressor); + + if (nError) { + return -1; + } + else { + return nCompressedSize; + } +} diff --git a/gbdk-support/gbcompress/zx0/zx0_shrink.h b/gbdk-support/gbcompress/zx0/zx0_shrink.h @@ -0,0 +1,168 @@ +/* + * shrink.h - compressor definitions + * + * Copyright (C) 2021 Emmanuel Marty + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +/* + * Uses the libdivsufsort library Copyright (c) 2003-2008 Yuta Mori + * + * Implements the ZX0 encoding designed by Einar Saukas. https://github.com/einar-saukas/ZX0 + * Also inspired by Charles Bloom's compression blog. http://cbloomrants.blogspot.com/ + * + */ + +#ifndef _SHRINK_H +#define _SHRINK_H + +#include "libdivsufsort/divsufsort.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LCP_BITS 18 +#define TAG_BITS 4 +#define LCP_MAX ((1U<<(LCP_BITS - TAG_BITS)) - 1) +#define LCP_AND_TAG_MAX ((1U<<LCP_BITS) - 1) +#define LCP_SHIFT (63-LCP_BITS) +#define LCP_MASK (((1ULL<<LCP_BITS) - 1) << LCP_SHIFT) +#define POS_MASK ((1ULL<<LCP_SHIFT) - 1) +#define VISITED_FLAG 0x8000000000000000ULL +#define EXCL_VISITED_MASK 0x7fffffffffffffffULL + +#define NINITIAL_ARRIVALS_PER_POSITION 40 +#define NMAX_ARRIVALS_PER_POSITION 109 +#define NMATCHES_PER_INDEX 78 + +#define LEAVE_ALONE_MATCH_SIZE 340 + +/** One match option */ +typedef struct _salvador_match { + unsigned short length; + unsigned short offset; +} salvador_match; + +/** Forward arrival slot */ +typedef struct _salvador_arrival { + int cost; + + unsigned int from_pos:17; + int from_slot:11; + + unsigned int rep_offset; + + unsigned int rep_pos:17; + unsigned int match_len:14; + + int num_literals; + int score; +} salvador_arrival; + +/** Visited match */ +typedef int salvador_visited; + +/** Compression statistics */ +typedef struct _salvador_stats { + int num_literals; + int num_normal_matches; + int num_rep_matches; + int num_eod; + + int safe_dist; + + int min_literals; + int max_literals; + int total_literals; + + int min_offset; + int max_offset; + long long total_offsets; + + int min_match_len; + int max_match_len; + int total_match_lens; + + int min_rle1_len; + int max_rle1_len; + int total_rle1_lens; + + int min_rle2_len; + int max_rle2_len; + int total_rle2_lens; + + int commands_divisor; + int literals_divisor; + int match_divisor; + int rle1_divisor; + int rle2_divisor; +} salvador_stats; + +/** Compression context */ +typedef struct _salvador_compressor { + divsufsort_ctx_t divsufsort_context; + unsigned long long *intervals; + unsigned long long *pos_data; + unsigned long long *open_intervals; + salvador_match *match; + unsigned short *match_depth; + salvador_match *best_match; + salvador_arrival *arrival; + int *first_offset_for_byte; + int *next_offset_for_pos; + int *offset_cache; + int flags; + int block_size; + int max_offset; + int max_arrivals_per_position; + salvador_stats stats; +} salvador_compressor; + +/** + * Get maximum compressed size of input(source) data + * + * @param nInputSize input(source) size in bytes + * + * @return maximum compressed size + */ +size_t salvador_get_max_compressed_size(const size_t nInputSize); + +/** + * Compress memory + * + * @param pInputData pointer to input(source) data to compress + * @param pOutBuffer buffer for compressed data + * @param nInputSize input(source) size in bytes + * @param nMaxOutBufferSize maximum capacity of compression buffer + * @param nFlags compression flags (set to FLG_IS_INVERTED) + * @param nMaxOffset maximum match offset to use (0 for default) + * @param nDictionarySize size of dictionary in front of input data (0 for none) + * @param progress progress function, called after compressing each block, or NULL for none + * @param pStats pointer to compression stats that are filled if this function is successful, or NULL + * + * @return actual compressed size, or -1 for error + */ +size_t salvador_compress(const unsigned char *pInputData, unsigned char *pOutBuffer, const size_t nInputSize, const size_t nMaxOutBufferSize, + const unsigned int nFlags, const size_t nMaxOffset, const size_t nDictionarySize, void(*progress)(long long nOriginalSize, long long nCompressedSize), salvador_stats *pStats); + +#ifdef __cplusplus +} +#endif + +#endif /* _SHRINK_H */ diff --git a/gbdk-support/png2asset/Makefile b/gbdk-support/png2asset/Makefile @@ -76,3 +76,6 @@ test-compare: test-reset-ref: $(MAKE) -C testing reset-ref + +test-clean-ref: + $(MAKE) -C testing clean-ref diff --git a/gbdk-support/png2asset/export_c_file.cpp b/gbdk-support/png2asset/export_c_file.cpp @@ -66,7 +66,8 @@ bool export_c_file( PNG2AssetData* assetData) { fprintf(file, "#pragma bank %d\n\n", assetData->args->bank); } - fprintf(file, "//AUTOGENERATED FILE FROM png2asset\n\n"); + fprintf(file, "//AUTOGENERATED FILE FROM png2asset\n"); + fprintf(file, "// Conversion args:%s\n\n", assetData->args->args_for_logging_to_output.c_str()); fprintf(file, "#include <stdint.h>\n"); fprintf(file, "#include <gbdk/platform.h>\n"); diff --git a/gbdk-support/png2asset/export_h_file.cpp b/gbdk-support/png2asset/export_h_file.cpp @@ -51,6 +51,8 @@ bool export_h_file( PNG2AssetData* assetData) { } fprintf(file, "//AUTOGENERATED FILE FROM png2asset\n"); + fprintf(file, "// Conversion args:%s\n\n", assetData->args->args_for_logging_to_output.c_str()); + fprintf(file, "#ifndef METASPRITE_%s_H\n", assetData->args->data_name.c_str()); fprintf(file, "#define METASPRITE_%s_H\n", assetData->args->data_name.c_str()); fprintf(file, "\n"); diff --git a/gbdk-support/png2asset/process_arguments.cpp b/gbdk-support/png2asset/process_arguments.cpp @@ -28,6 +28,19 @@ using namespace std; +// Strip any leading path and slashes +string str_remove_path(string str_in) { + size_t slash_pos = str_in.find_last_of('/'); + if (slash_pos != str_in.npos) + str_in = str_in.substr(slash_pos, str_in.length() - slash_pos); + + slash_pos = str_in.find_last_of('\\'); + if (slash_pos != str_in.npos) + str_in = str_in.substr(slash_pos, str_in.length() - slash_pos); + + return str_in; +} + int processPNG2AssetArguments(int argc, char* argv[], PNG2AssetArguments* args) { //default values for some params @@ -82,6 +95,8 @@ int processPNG2AssetArguments(int argc, char* argv[], PNG2AssetArguments* args) args->pack_mode = Tile::GB; args->map_entry_size_bytes = 1; + args->args_for_logging_to_output = ""; + args->relative_paths = false; if(argc < 2) @@ -124,7 +139,7 @@ int processPNG2AssetArguments(int argc, char* argv[], PNG2AssetArguments* args) printf("-keep_duplicate_tiles do not remove duplicate tiles (default: not enabled)\n"); printf("-no_palettes do not export palette data\n"); - printf("-bin export to binary format\n"); + printf("-bin export to binary format (requires -map)\n"); printf("-transposed export transposed (column-by-column instead of row-by-row)\n"); printf("-rel_paths paths to tilesets are relative to the input file path\n"); @@ -136,6 +151,11 @@ int processPNG2AssetArguments(int argc, char* argv[], PNG2AssetArguments* args) args->output_filename = argv[1]; args->output_filename = args->output_filename.substr(0, args->output_filename.size() - 4) + ".c"; + // Save all args for logging into output files + for(int i = 1; i < argc; ++i) { + args->args_for_logging_to_output.append(" ").append( str_remove_path((string)argv[i]) ); + } + //Parse argv for(int i = 2; i < argc; ++i) { @@ -348,6 +368,11 @@ int processPNG2AssetArguments(int argc, char* argv[], PNG2AssetArguments* args) return EXIT_FAILURE; } + if((args->output_binary) && (args->export_as_map == false)) { + printf("Error: \"-bin\" export mode only works when \"-map\" is enabled\n"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; } diff --git a/gbdk-support/png2asset/process_arguments.h b/gbdk-support/png2asset/process_arguments.h @@ -72,6 +72,8 @@ struct PNG2AssetArguments { bool has_entity_tileset; int processing_mode; // Whether the current image being processed is a source tileset (MODE_SOURCE_TILESET), entity_tileset (MODE_ENTITY_TILESET), or the main image (MODE_MAIN_IMAGE) + string args_for_logging_to_output; + Tile::PackMode pack_mode; int map_entry_size_bytes; diff --git a/gbdk-support/png2asset/testing/Makefile b/gbdk-support/png2asset/testing/Makefile @@ -26,6 +26,9 @@ compare-output: clean: rm -rf $(OUTDIR) +clean-ref: + rm -rf $(REF) + reset-ref: rm -rf $(REF) git checkout $(REF)/* @@ -146,9 +149,10 @@ generate-assets: $(PNG2ASSET) $(RES)/SGB/sgb_border_multi_pal.png -c $(OUTDIR)/sgb_border_multi_pal.c -map -bpp 4 -max_palettes 4 -pack_mode sgb -use_map_attributes # -bin output - $(PNG2ASSET) $(RES)/gameboy.png -c $(OUTDIR)/binout.1.bin -bin -tiles_only - $(PNG2ASSET) $(RES)/gameboy.png -c $(OUTDIR)/binout.2.bin -bin -maps_only - $(PNG2ASSET) $(RES)/gameboy.png -c $(OUTDIR)/binout.3.bin -bin -maps_only -no_palettes + $(PNG2ASSET) $(RES)/gameboy.png -o $(OUTDIR)/binout_basic.bin -map -bin + $(PNG2ASSET) $(RES)/gameboy.png -o $(OUTDIR)/binout_tileonly.bin -map -bin -tiles_only + $(PNG2ASSET) $(RES)/gameboy.png -o $(OUTDIR)/binout_maponly.bin -map -bin -maps_only + $(PNG2ASSET) $(RES)/gameboy.png -o $(OUTDIR)/binout_maponly-nopal.bin -map -bin -maps_only -no_palettes # -no_palettes $(PNG2ASSET) $(RES)/no_pal_map_test.png -c $(OUTDIR)/no_pal_map_test_keep_pal_order.c -noflip -map -keep_palette_order -no_palettes diff --git a/gbdk-support/png2asset/testing/ref/apaimage_1.c b/gbdk-support/png2asset/testing/ref/apaimage_1.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /apa_image.png -c /apaimage_1.c -map -keep_duplicate_tiles -bpp 2 -tiles_only #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/apaimage_1.h b/gbdk-support/png2asset/testing/ref/apaimage_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /apa_image.png -c /apaimage_1.c -map -keep_duplicate_tiles -bpp 2 -tiles_only + #ifndef METASPRITE_apaimage_1_H #define METASPRITE_apaimage_1_H diff --git a/gbdk-support/png2asset/testing/ref/binout.1.bin b/gbdk-support/png2asset/testing/ref/binout.1.bin @@ -1,83 +0,0 @@ -//AUTOGENERATED FILE FROM png2asset - -#include <stdint.h> -#include <gbdk/platform.h> -#include <gbdk/metasprites.h> - -BANKREF(binout) - -const palette_color_t binout_palettes[4] = { - RGB8(255,254,245), RGB8(224,223,175), RGB8(172,182,127), RGB8( 63, 67, 40) - }; - -const uint8_t binout_tiles[1088] = { - 0x3f,0x3f,0x60,0x7f,0xc0,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0, - 0x80,0xc0,0x83,0xc3,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7, - 0xff,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0xff,0xff,0xf8,0xff,0xf8, - 0xff,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0x00, - 0xff,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,0xff,0xef,0x1f,0xef,0x1f, - 0xfc,0xfc,0x06,0xfe,0x03,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03, - 0x01,0x03,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3, - 0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x82,0xc7,0x83,0xc7, - 0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7, - 0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8,0x7f,0xf8,0xbf,0x78,0x7f,0xf8, - 0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8, - 0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00, - 0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00, - 0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f, - 0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f, - 0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3, - 0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3, - 0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7, - 0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x83,0xc7,0x80,0xc3,0x80,0xc0,0x80,0xc0, - 0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8,0xff,0xf8, - 0xf8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0x00,0x00, - 0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00, - 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0x00,0x00, - 0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f,0xef,0x1f, - 0x0f,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfc,0xfc,0x00,0xf8,0x00,0x00,0x00,0x00, - 0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3,0xc1,0xc3, - 0x81,0xc3,0x81,0x83,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03, - 0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0, - 0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc1,0x83,0xc3, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x1e,0x1e,0x1e,0x3f,0x1e,0x3f,0x1e,0x3f,0x3f,0xff,0xff,0xff, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0xf0,0xf0, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0e,0x0f, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x07,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f, - 0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03, - 0x01,0x03,0x01,0x03,0x01,0x03,0x81,0x83,0xc1,0xc3,0xc1,0xe3,0xc1,0xe3,0xc1,0xc3, - 0x83,0xc3,0x83,0xc3,0x83,0xc3,0x80,0xc3,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0, - 0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0, - 0xff,0xff,0xff,0xff,0xff,0xff,0x3f,0xff,0x1e,0x3f,0x1e,0x3f,0x1e,0x3f,0x1e,0x3f, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x00,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x07,0x0f,0x1c,0x1e, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x81,0x03,0x87,0x0e,0x1e, - 0x1f,0x1f,0x3f,0x3f,0x3f,0x3f,0x1f,0x3f,0x1f,0x1f,0x04,0x0e,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0x80,0x80,0x00,0x00, - 0x07,0x87,0x80,0x83,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x04,0x04,0x04, - 0x81,0xc3,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03, - 0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x03,0x01,0x83,0x81,0x83,0x41,0x43, - 0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0, - 0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0x80,0xc0,0xc0,0xc0,0x60,0x7f,0x3f,0x3f, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, - 0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, - 0x00,0x00,0x01,0x01,0x00,0x01,0x00,0x08,0x04,0x04,0x20,0x26,0x02,0x32,0x10,0x11, - 0x01,0x19,0x08,0x08,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff, - 0x20,0x22,0x02,0x32,0x11,0x91,0x81,0x89,0x08,0x48,0x44,0x44,0x24,0x24,0x22,0x22, - 0x10,0x10,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x01,0x07,0xff,0xfc,0xfc, - 0x41,0x43,0x21,0x23,0x01,0x23,0x11,0x13,0x81,0x93,0x03,0x83,0x02,0x02,0x02,0x06, - 0x06,0x06,0x0c,0x0c,0x0c,0x1c,0x18,0x38,0x70,0x70,0xe0,0xe0,0x80,0x80,0x00,0x00 - }; - diff --git a/gbdk-support/png2asset/testing/ref/binout.2.bin b/gbdk-support/png2asset/testing/ref/binout.2.bin @@ -1,59 +0,0 @@ -//AUTOGENERATED FILE FROM png2asset - -#include <stdint.h> -#include <gbdk/platform.h> -#include <gbdk/metasprites.h> - -BANKREF(binout) - -const palette_color_t binout_palettes[4] = { - RGB8(255,254,245), RGB8(224,223,175), RGB8(172,182,127), RGB8( 63, 67, 40) - }; -const metasprite_t binout_metasprite0[] = { - METASPR_ITEM(-48, -28, 0, S_PAL(0)), - METASPR_ITEM(0, 8, 2, S_PAL(0)), - METASPR_ITEM(0, 8, 4, S_PAL(0)), - METASPR_ITEM(0, 8, 4, S_PAL(0)), - METASPR_ITEM(0, 8, 4, S_PAL(0)), - METASPR_ITEM(0, 8, 6, S_PAL(0)), - METASPR_ITEM(0, 8, 8, S_PAL(0)), - METASPR_ITEM(16, -48, 10, S_PAL(0)), - METASPR_ITEM(0, 8, 12, S_PAL(0)), - METASPR_ITEM(0, 8, 14, S_PAL(0)), - METASPR_ITEM(0, 8, 14, S_PAL(0)), - METASPR_ITEM(0, 8, 14, S_PAL(0)), - METASPR_ITEM(0, 8, 16, S_PAL(0)), - METASPR_ITEM(0, 8, 18, S_PAL(0)), - METASPR_ITEM(16, -48, 20, S_PAL(0)), - METASPR_ITEM(0, 8, 22, S_PAL(0)), - METASPR_ITEM(0, 8, 24, S_PAL(0)), - METASPR_ITEM(0, 8, 24, S_PAL(0)), - METASPR_ITEM(0, 8, 24, S_PAL(0)), - METASPR_ITEM(0, 8, 26, S_PAL(0)), - METASPR_ITEM(0, 8, 28, S_PAL(0)), - METASPR_ITEM(16, -48, 30, S_PAL(0)), - METASPR_ITEM(0, 8, 32, S_PAL(0)), - METASPR_ITEM(0, 8, 34, S_PAL(0)), - METASPR_ITEM(0, 16, 36, S_PAL(0)), - METASPR_ITEM(0, 8, 38, S_PAL(0)), - METASPR_ITEM(0, 8, 40, S_PAL(0)), - METASPR_ITEM(16, -48, 42, S_PAL(0)), - METASPR_ITEM(0, 8, 44, S_PAL(0)), - METASPR_ITEM(0, 8, 46, S_PAL(0)), - METASPR_ITEM(0, 8, 48, S_PAL(0)), - METASPR_ITEM(0, 8, 50, S_PAL(0)), - METASPR_ITEM(0, 8, 52, S_PAL(0)), - METASPR_ITEM(0, 8, 54, S_PAL(0)), - METASPR_ITEM(16, -48, 56, S_PAL(0)), - METASPR_ITEM(0, 8, 58, S_PAL(0)), - METASPR_ITEM(0, 8, 60, S_PAL(0)), - METASPR_ITEM(0, 8, 60, S_PAL(0) | S_FLIPX), - METASPR_ITEM(0, 8, 62, S_PAL(0)), - METASPR_ITEM(0, 8, 64, S_PAL(0)), - METASPR_ITEM(0, 8, 66, S_PAL(0)), - METASPR_TERM -}; - -const metasprite_t* const binout_metasprites[1] = { - binout_metasprite0 -}; diff --git a/gbdk-support/png2asset/testing/ref/binout.3.bin b/gbdk-support/png2asset/testing/ref/binout.3.bin @@ -1,56 +0,0 @@ -//AUTOGENERATED FILE FROM png2asset - -#include <stdint.h> -#include <gbdk/platform.h> -#include <gbdk/metasprites.h> - -BANKREF(binout) - -const metasprite_t binout_metasprite0[] = { - METASPR_ITEM(-48, -28, 0, S_PAL(0)), - METASPR_ITEM(0, 8, 2, S_PAL(0)), - METASPR_ITEM(0, 8, 4, S_PAL(0)), - METASPR_ITEM(0, 8, 4, S_PAL(0)), - METASPR_ITEM(0, 8, 4, S_PAL(0)), - METASPR_ITEM(0, 8, 6, S_PAL(0)), - METASPR_ITEM(0, 8, 8, S_PAL(0)), - METASPR_ITEM(16, -48, 10, S_PAL(0)), - METASPR_ITEM(0, 8, 12, S_PAL(0)), - METASPR_ITEM(0, 8, 14, S_PAL(0)), - METASPR_ITEM(0, 8, 14, S_PAL(0)), - METASPR_ITEM(0, 8, 14, S_PAL(0)), - METASPR_ITEM(0, 8, 16, S_PAL(0)), - METASPR_ITEM(0, 8, 18, S_PAL(0)), - METASPR_ITEM(16, -48, 20, S_PAL(0)), - METASPR_ITEM(0, 8, 22, S_PAL(0)), - METASPR_ITEM(0, 8, 24, S_PAL(0)), - METASPR_ITEM(0, 8, 24, S_PAL(0)), - METASPR_ITEM(0, 8, 24, S_PAL(0)), - METASPR_ITEM(0, 8, 26, S_PAL(0)), - METASPR_ITEM(0, 8, 28, S_PAL(0)), - METASPR_ITEM(16, -48, 30, S_PAL(0)), - METASPR_ITEM(0, 8, 32, S_PAL(0)), - METASPR_ITEM(0, 8, 34, S_PAL(0)), - METASPR_ITEM(0, 16, 36, S_PAL(0)), - METASPR_ITEM(0, 8, 38, S_PAL(0)), - METASPR_ITEM(0, 8, 40, S_PAL(0)), - METASPR_ITEM(16, -48, 42, S_PAL(0)), - METASPR_ITEM(0, 8, 44, S_PAL(0)), - METASPR_ITEM(0, 8, 46, S_PAL(0)), - METASPR_ITEM(0, 8, 48, S_PAL(0)), - METASPR_ITEM(0, 8, 50, S_PAL(0)), - METASPR_ITEM(0, 8, 52, S_PAL(0)), - METASPR_ITEM(0, 8, 54, S_PAL(0)), - METASPR_ITEM(16, -48, 56, S_PAL(0)), - METASPR_ITEM(0, 8, 58, S_PAL(0)), - METASPR_ITEM(0, 8, 60, S_PAL(0)), - METASPR_ITEM(0, 8, 60, S_PAL(0) | S_FLIPX), - METASPR_ITEM(0, 8, 62, S_PAL(0)), - METASPR_ITEM(0, 8, 64, S_PAL(0)), - METASPR_ITEM(0, 8, 66, S_PAL(0)), - METASPR_TERM -}; - -const metasprite_t* const binout_metasprites[1] = { - binout_metasprite0 -}; diff --git a/gbdk-support/png2asset/testing/ref/binout.h b/gbdk-support/png2asset/testing/ref/binout.h @@ -1,24 +0,0 @@ -//AUTOGENERATED FILE FROM png2asset -#ifndef METASPRITE_binout_H -#define METASPRITE_binout_H - -#include <stdint.h> -#include <gbdk/platform.h> -#include <gbdk/metasprites.h> - -#define binout_TILE_ORIGIN 0 -#define binout_TILE_W 8 -#define binout_TILE_H 16 -#define binout_WIDTH 56 -#define binout_HEIGHT 96 -#define binout_TILE_COUNT 68 -#define binout_PIVOT_X 28 -#define binout_PIVOT_Y 48 -#define binout_PIVOT_W 56 -#define binout_PIVOT_H 96 -extern const metasprite_t* const binout_metasprites[1]; - -BANKREF_EXTERN(binout) - - -#endif diff --git a/gbdk-support/png2asset/testing/ref/binout_basic.h b/gbdk-support/png2asset/testing/ref/binout_basic.h @@ -0,0 +1,29 @@ +//AUTOGENERATED FILE FROM png2asset +// Conversion args: /gameboy.png -o /binout_basic.bin -map -bin + +#ifndef METASPRITE_binout_basic_H +#define METASPRITE_binout_basic_H + +#include <stdint.h> +#include <gbdk/platform.h> +#include <gbdk/metasprites.h> + +#define binout_basic_TILE_ORIGIN 0 +#define binout_basic_TILE_W 8 +#define binout_basic_TILE_H 8 +#define binout_basic_WIDTH 56 +#define binout_basic_HEIGHT 96 +#define binout_basic_TILE_COUNT 45 +#define binout_basic_PALETTE_COUNT 1 +#define binout_basic_COLORS_PER_PALETTE 4 +#define binout_basic_TOTAL_COLORS 4 +#define binout_basic_MAP_ATTRIBUTES 0 +extern const unsigned char binout_basic_map[84]; +#define binout_basic_map_attributes binout_basic_map + +BANKREF_EXTERN(binout_basic) + +extern const palette_color_t binout_basic_palettes[4]; +extern const uint8_t binout_basic_tiles[720]; + +#endif diff --git a/gbdk-support/png2asset/testing/ref/binout_basic_map.bin b/gbdk-support/png2asset/testing/ref/binout_basic_map.bin Binary files differ. diff --git a/gbdk-support/png2asset/testing/ref/binout_basic_palettes.bin b/gbdk-support/png2asset/testing/ref/binout_basic_palettes.bin @@ -0,0 +1 @@ +ÿ{|WÕ> + diff --git a/gbdk-support/png2asset/testing/ref/binout_basic_tiles.bin b/gbdk-support/png2asset/testing/ref/binout_basic_tiles.bin Binary files differ. diff --git a/gbdk-support/png2asset/testing/ref/binout_maponly-nopal.h b/gbdk-support/png2asset/testing/ref/binout_maponly-nopal.h @@ -0,0 +1,24 @@ +//AUTOGENERATED FILE FROM png2asset +// Conversion args: /gameboy.png -o /binout_maponly-nopal.bin -map -bin -maps_only -no_palettes + +#ifndef METASPRITE_binout_maponly_nopal_H +#define METASPRITE_binout_maponly_nopal_H + +#include <stdint.h> +#include <gbdk/platform.h> +#include <gbdk/metasprites.h> + +#define binout_maponly_nopal_TILE_ORIGIN 0 +#define binout_maponly_nopal_TILE_W 8 +#define binout_maponly_nopal_TILE_H 8 +#define binout_maponly_nopal_WIDTH 56 +#define binout_maponly_nopal_HEIGHT 96 +#define binout_maponly_nopal_TILE_COUNT 45 +#define binout_maponly_nopal_MAP_ATTRIBUTES 0 +extern const unsigned char binout_maponly_nopal_map[84]; +#define binout_maponly_nopal_map_attributes binout_maponly_nopal_map + +BANKREF_EXTERN(binout_maponly_nopal) + + +#endif diff --git a/gbdk-support/png2asset/testing/ref/binout_maponly-nopal_map.bin b/gbdk-support/png2asset/testing/ref/binout_maponly-nopal_map.bin Binary files differ. diff --git a/gbdk-support/png2asset/testing/ref/binout_maponly.h b/gbdk-support/png2asset/testing/ref/binout_maponly.h @@ -0,0 +1,28 @@ +//AUTOGENERATED FILE FROM png2asset +// Conversion args: /gameboy.png -o /binout_maponly.bin -map -bin -maps_only + +#ifndef METASPRITE_binout_maponly_H +#define METASPRITE_binout_maponly_H + +#include <stdint.h> +#include <gbdk/platform.h> +#include <gbdk/metasprites.h> + +#define binout_maponly_TILE_ORIGIN 0 +#define binout_maponly_TILE_W 8 +#define binout_maponly_TILE_H 8 +#define binout_maponly_WIDTH 56 +#define binout_maponly_HEIGHT 96 +#define binout_maponly_TILE_COUNT 45 +#define binout_maponly_PALETTE_COUNT 1 +#define binout_maponly_COLORS_PER_PALETTE 4 +#define binout_maponly_TOTAL_COLORS 4 +#define binout_maponly_MAP_ATTRIBUTES 0 +extern const unsigned char binout_maponly_map[84]; +#define binout_maponly_map_attributes binout_maponly_map + +BANKREF_EXTERN(binout_maponly) + +extern const palette_color_t binout_maponly_palettes[4]; + +#endif diff --git a/gbdk-support/png2asset/testing/ref/binout_maponly_map.bin b/gbdk-support/png2asset/testing/ref/binout_maponly_map.bin Binary files differ. diff --git a/gbdk-support/png2asset/testing/ref/binout_maponly_palettes.bin b/gbdk-support/png2asset/testing/ref/binout_maponly_palettes.bin @@ -0,0 +1 @@ +ÿ{|WÕ> + diff --git a/gbdk-support/png2asset/testing/ref/binout_tileonly.h b/gbdk-support/png2asset/testing/ref/binout_tileonly.h @@ -0,0 +1,26 @@ +//AUTOGENERATED FILE FROM png2asset +// Conversion args: /gameboy.png -o /binout_tileonly.bin -map -bin -tiles_only + +#ifndef METASPRITE_binout_tileonly_H +#define METASPRITE_binout_tileonly_H + +#include <stdint.h> +#include <gbdk/platform.h> +#include <gbdk/metasprites.h> + +#define binout_tileonly_TILE_ORIGIN 0 +#define binout_tileonly_TILE_W 8 +#define binout_tileonly_TILE_H 8 +#define binout_tileonly_WIDTH 56 +#define binout_tileonly_HEIGHT 96 +#define binout_tileonly_TILE_COUNT 45 +#define binout_tileonly_PALETTE_COUNT 1 +#define binout_tileonly_COLORS_PER_PALETTE 4 +#define binout_tileonly_TOTAL_COLORS 4 + +BANKREF_EXTERN(binout_tileonly) + +extern const palette_color_t binout_tileonly_palettes[4]; +extern const uint8_t binout_tileonly_tiles[720]; + +#endif diff --git a/gbdk-support/png2asset/testing/ref/binout_tileonly_palettes.bin b/gbdk-support/png2asset/testing/ref/binout_tileonly_palettes.bin @@ -0,0 +1 @@ +ÿ{|WÕ> + diff --git a/gbdk-support/png2asset/testing/ref/binout_tileonly_tiles.bin b/gbdk-support/png2asset/testing/ref/binout_tileonly_tiles.bin Binary files differ. diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_1.c b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_1.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_1.c -map -use_map_attributes -keep_palette_order #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_1.h b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_1.c -map -use_map_attributes -keep_palette_order + #ifndef METASPRITE_color_indexed_nums_map_1_H #define METASPRITE_color_indexed_nums_map_1_H diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_2.c b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_2.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_2.c -map -use_map_attributes -keep_palette_order -source_tileset /color_indexed_nums8x8_9_to_0.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_2.h b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_2.c -map -use_map_attributes -keep_palette_order -source_tileset /color_indexed_nums8x8_9_to_0.png + #ifndef METASPRITE_color_indexed_nums_map_2_H #define METASPRITE_color_indexed_nums_map_2_H diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_3.c b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_3.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_3.c -map -use_map_attributes -keep_palette_order -entity_tileset /color_indexed_nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_3.h b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_3.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_3.c -map -use_map_attributes -keep_palette_order -entity_tileset /color_indexed_nums8x8_30_to_39.png + #ifndef METASPRITE_color_indexed_nums_map_3_H #define METASPRITE_color_indexed_nums_map_3_H diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_4.c b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_4.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_4.c -map -use_map_attributes -keep_palette_order -source_tileset /color_indexed_nums8x8_9_to_0.png -entity_tileset /color_indexed_nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_4.h b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_4.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_4.c -map -use_map_attributes -keep_palette_order -source_tileset /color_indexed_nums8x8_9_to_0.png -entity_tileset /color_indexed_nums8x8_30_to_39.png + #ifndef METASPRITE_color_indexed_nums_map_4_H #define METASPRITE_color_indexed_nums_map_4_H diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_5.c b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_5.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_5.c -map -use_map_attributes -keep_palette_order #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_5.h b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_5.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_5.c -map -use_map_attributes -keep_palette_order + #ifndef METASPRITE_color_indexed_nums_map_5_H #define METASPRITE_color_indexed_nums_map_5_H diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_6.c b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_6.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_6.c -map -use_map_attributes -keep_palette_order -keep_duplicate_tiles -source_tileset /color_indexed_nums8x8_9_to_0.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_6.h b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_6.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_6.c -map -use_map_attributes -keep_palette_order -keep_duplicate_tiles -source_tileset /color_indexed_nums8x8_9_to_0.png + #ifndef METASPRITE_color_indexed_nums_map_6_H #define METASPRITE_color_indexed_nums_map_6_H diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_7.c b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_7.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_7.c -map -use_map_attributes -keep_palette_order -keep_duplicate_tiles -entity_tileset /color_indexed_nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_7.h b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_7.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_7.c -map -use_map_attributes -keep_palette_order -keep_duplicate_tiles -entity_tileset /color_indexed_nums8x8_30_to_39.png + #ifndef METASPRITE_color_indexed_nums_map_7_H #define METASPRITE_color_indexed_nums_map_7_H diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_8.c b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_8.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_8.c -map -use_map_attributes -keep_palette_order -keep_duplicate_tiles -source_tileset /color_indexed_nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_8.h b/gbdk-support/png2asset/testing/ref/color_indexed_nums_map_8.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /color_indexed_nums_map_8.c -map -use_map_attributes -keep_palette_order -keep_duplicate_tiles -source_tileset /color_indexed_nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png + #ifndef METASPRITE_color_indexed_nums_map_8_H #define METASPRITE_color_indexed_nums_map_8_H diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_1.c b/gbdk-support/png2asset/testing/ref/color_nums_map_1.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_1.c -map -use_map_attributes #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_1.h b/gbdk-support/png2asset/testing/ref/color_nums_map_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_1.c -map -use_map_attributes + #ifndef METASPRITE_color_nums_map_1_H #define METASPRITE_color_nums_map_1_H diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_2.c b/gbdk-support/png2asset/testing/ref/color_nums_map_2.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_2.c -map -use_map_attributes -source_tileset /color_nums8x8_9_to_0.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_2.h b/gbdk-support/png2asset/testing/ref/color_nums_map_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_2.c -map -use_map_attributes -source_tileset /color_nums8x8_9_to_0.png + #ifndef METASPRITE_color_nums_map_2_H #define METASPRITE_color_nums_map_2_H diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_3.c b/gbdk-support/png2asset/testing/ref/color_nums_map_3.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_3.c -map -use_map_attributes -entity_tileset /color_nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_3.h b/gbdk-support/png2asset/testing/ref/color_nums_map_3.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_3.c -map -use_map_attributes -entity_tileset /color_nums8x8_30_to_39.png + #ifndef METASPRITE_color_nums_map_3_H #define METASPRITE_color_nums_map_3_H diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_4.c b/gbdk-support/png2asset/testing/ref/color_nums_map_4.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_4.c -map -use_map_attributes -source_tileset /color_nums8x8_9_to_0.png -entity_tileset /color_nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_4.h b/gbdk-support/png2asset/testing/ref/color_nums_map_4.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_4.c -map -use_map_attributes -source_tileset /color_nums8x8_9_to_0.png -entity_tileset /color_nums8x8_30_to_39.png + #ifndef METASPRITE_color_nums_map_4_H #define METASPRITE_color_nums_map_4_H diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_5.c b/gbdk-support/png2asset/testing/ref/color_nums_map_5.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_5.c -map -use_map_attributes #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_5.h b/gbdk-support/png2asset/testing/ref/color_nums_map_5.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_5.c -map -use_map_attributes + #ifndef METASPRITE_color_nums_map_5_H #define METASPRITE_color_nums_map_5_H diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_6.c b/gbdk-support/png2asset/testing/ref/color_nums_map_6.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_6.c -map -use_map_attributes -keep_duplicate_tiles -source_tileset /color_nums8x8_9_to_0.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_6.h b/gbdk-support/png2asset/testing/ref/color_nums_map_6.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_6.c -map -use_map_attributes -keep_duplicate_tiles -source_tileset /color_nums8x8_9_to_0.png + #ifndef METASPRITE_color_nums_map_6_H #define METASPRITE_color_nums_map_6_H diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_7.c b/gbdk-support/png2asset/testing/ref/color_nums_map_7.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_7.c -map -use_map_attributes -keep_duplicate_tiles -entity_tileset /color_nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_7.h b/gbdk-support/png2asset/testing/ref/color_nums_map_7.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_7.c -map -use_map_attributes -keep_duplicate_tiles -entity_tileset /color_nums8x8_30_to_39.png + #ifndef METASPRITE_color_nums_map_7_H #define METASPRITE_color_nums_map_7_H diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_8.c b/gbdk-support/png2asset/testing/ref/color_nums_map_8.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_8.c -map -use_map_attributes -keep_duplicate_tiles -source_tileset /color_nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/color_nums_map_8.h b/gbdk-support/png2asset/testing/ref/color_nums_map_8.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /color_nums_map_8.c -map -use_map_attributes -keep_duplicate_tiles -source_tileset /color_nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png + #ifndef METASPRITE_color_nums_map_8_H #define METASPRITE_color_nums_map_8_H diff --git a/gbdk-support/png2asset/testing/ref/gbdklogo_1.c b/gbdk-support/png2asset/testing/ref/gbdklogo_1.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /GBDK_2020_logo.png -o /gbdklogo_1.c -map -bpp 2 -max_palettes 1 -pack_mode gb -noflip #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/gbdklogo_1.h b/gbdk-support/png2asset/testing/ref/gbdklogo_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /GBDK_2020_logo.png -o /gbdklogo_1.c -map -bpp 2 -max_palettes 1 -pack_mode gb -noflip + #ifndef METASPRITE_gbdklogo_1_H #define METASPRITE_gbdklogo_1_H diff --git a/gbdk-support/png2asset/testing/ref/gbdklogo_2.c b/gbdk-support/png2asset/testing/ref/gbdklogo_2.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /GBDK_2020_logo.png -o /gbdklogo_2.c -map -use_map_attributes -bpp 2 -max_palettes 8 -pack_mode gb #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/gbdklogo_2.h b/gbdk-support/png2asset/testing/ref/gbdklogo_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /GBDK_2020_logo.png -o /gbdklogo_2.c -map -use_map_attributes -bpp 2 -max_palettes 8 -pack_mode gb + #ifndef METASPRITE_gbdklogo_2_H #define METASPRITE_gbdklogo_2_H diff --git a/gbdk-support/png2asset/testing/ref/gbdklogo_3.c b/gbdk-support/png2asset/testing/ref/gbdklogo_3.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /GBDK_2020_logo.png -o /gbdklogo_3.c -map -use_nes_attributes -bpp 2 -max_palettes 4 -pack_mode nes -noflip -use_nes_colors #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/gbdklogo_3.h b/gbdk-support/png2asset/testing/ref/gbdklogo_3.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /GBDK_2020_logo.png -o /gbdklogo_3.c -map -use_nes_attributes -bpp 2 -max_palettes 4 -pack_mode nes -noflip -use_nes_colors + #ifndef METASPRITE_gbdklogo_3_H #define METASPRITE_gbdklogo_3_H diff --git a/gbdk-support/png2asset/testing/ref/gbdklogo_4.c b/gbdk-support/png2asset/testing/ref/gbdklogo_4.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /GBDK_2020_logo.png -o /gbdklogo_4.c -map -use_map_attributes -bpp 4 -max_palettes 2 -pack_mode sms #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/gbdklogo_4.h b/gbdk-support/png2asset/testing/ref/gbdklogo_4.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /GBDK_2020_logo.png -o /gbdklogo_4.c -map -use_map_attributes -bpp 4 -max_palettes 2 -pack_mode sms + #ifndef METASPRITE_gbdklogo_4_H #define METASPRITE_gbdklogo_4_H diff --git a/gbdk-support/png2asset/testing/ref/hblankcopy_1.c b/gbdk-support/png2asset/testing/ref/hblankcopy_1.c @@ -1,6 +1,7 @@ #pragma bank 255 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /hblank_copy.png -o /hblankcopy_1.c -map -noflip -tiles_only -keep_duplicate_tiles -b 255 #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/hblankcopy_1.h b/gbdk-support/png2asset/testing/ref/hblankcopy_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /hblank_copy.png -o /hblankcopy_1.c -map -noflip -tiles_only -keep_duplicate_tiles -b 255 + #ifndef METASPRITE_hblankcopy_1_H #define METASPRITE_hblankcopy_1_H diff --git a/gbdk-support/png2asset/testing/ref/largemap_1.c b/gbdk-support/png2asset/testing/ref/largemap_1.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_1.c -map -bpp 2 -max_palettes 4 -pack_mode gb -noflip #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/largemap_1.h b/gbdk-support/png2asset/testing/ref/largemap_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_1.c -map -bpp 2 -max_palettes 4 -pack_mode gb -noflip + #ifndef METASPRITE_largemap_1_H #define METASPRITE_largemap_1_H diff --git a/gbdk-support/png2asset/testing/ref/largemap_2.c b/gbdk-support/png2asset/testing/ref/largemap_2.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_2.c -map -bpp 2 -max_palettes 8 -use_map_attributes -pack_mode gb #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/largemap_2.h b/gbdk-support/png2asset/testing/ref/largemap_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_2.c -map -bpp 2 -max_palettes 8 -use_map_attributes -pack_mode gb + #ifndef METASPRITE_largemap_2_H #define METASPRITE_largemap_2_H diff --git a/gbdk-support/png2asset/testing/ref/largemap_3.c b/gbdk-support/png2asset/testing/ref/largemap_3.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_3.c -map -bpp 2 -max_palettes 4 -pack_mode gb -noflip #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/largemap_3.h b/gbdk-support/png2asset/testing/ref/largemap_3.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_3.c -map -bpp 2 -max_palettes 4 -pack_mode gb -noflip + #ifndef METASPRITE_largemap_3_H #define METASPRITE_largemap_3_H diff --git a/gbdk-support/png2asset/testing/ref/largemap_4.c b/gbdk-support/png2asset/testing/ref/largemap_4.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_4.c -map -bpp 2 -max_palettes 4 -pack_mode gb -noflip #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/largemap_4.h b/gbdk-support/png2asset/testing/ref/largemap_4.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_4.c -map -bpp 2 -max_palettes 4 -pack_mode gb -noflip + #ifndef METASPRITE_largemap_4_H #define METASPRITE_largemap_4_H diff --git a/gbdk-support/png2asset/testing/ref/largemap_5.c b/gbdk-support/png2asset/testing/ref/largemap_5.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_5.c -map -bpp 4 -max_palettes 2 -use_map_attributes -pack_mode sms #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/largemap_5.h b/gbdk-support/png2asset/testing/ref/largemap_5.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_5.c -map -bpp 4 -max_palettes 2 -use_map_attributes -pack_mode sms + #ifndef METASPRITE_largemap_5_H #define METASPRITE_largemap_5_H diff --git a/gbdk-support/png2asset/testing/ref/largemap_6.c b/gbdk-support/png2asset/testing/ref/largemap_6.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_6.c -map -bpp 4 -max_palettes 2 -use_map_attributes -pack_mode sms #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/largemap_6.h b/gbdk-support/png2asset/testing/ref/largemap_6.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_6.c -map -bpp 4 -max_palettes 2 -use_map_attributes -pack_mode sms + #ifndef METASPRITE_largemap_6_H #define METASPRITE_largemap_6_H diff --git a/gbdk-support/png2asset/testing/ref/largemap_7.c b/gbdk-support/png2asset/testing/ref/largemap_7.c @@ -1,6 +1,7 @@ #pragma bank 0 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_7.c -map -bpp 2 -max_palettes 4 -use_nes_attributes -pack_mode nes -noflip -b 0 #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/largemap_7.h b/gbdk-support/png2asset/testing/ref/largemap_7.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /largemap_7.c -map -bpp 2 -max_palettes 4 -use_nes_attributes -pack_mode nes -noflip -b 0 + #ifndef METASPRITE_largemap_7_H #define METASPRITE_largemap_7_H diff --git a/gbdk-support/png2asset/testing/ref/no_pal_map_test.c b/gbdk-support/png2asset/testing/ref/no_pal_map_test.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /no_pal_map_test.png -c /no_pal_map_test.c -noflip -map -no_palettes #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/no_pal_map_test.h b/gbdk-support/png2asset/testing/ref/no_pal_map_test.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /no_pal_map_test.png -c /no_pal_map_test.c -noflip -map -no_palettes + #ifndef METASPRITE_no_pal_map_test_H #define METASPRITE_no_pal_map_test_H diff --git a/gbdk-support/png2asset/testing/ref/no_pal_map_test_keep_pal_order.c b/gbdk-support/png2asset/testing/ref/no_pal_map_test_keep_pal_order.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /no_pal_map_test.png -c /no_pal_map_test_keep_pal_order.c -noflip -map -keep_palette_order -no_palettes #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/no_pal_map_test_keep_pal_order.h b/gbdk-support/png2asset/testing/ref/no_pal_map_test_keep_pal_order.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /no_pal_map_test.png -c /no_pal_map_test_keep_pal_order.c -noflip -map -keep_palette_order -no_palettes + #ifndef METASPRITE_no_pal_map_test_keep_pal_order_H #define METASPRITE_no_pal_map_test_keep_pal_order_H diff --git a/gbdk-support/png2asset/testing/ref/nums_map_1.c b/gbdk-support/png2asset/testing/ref/nums_map_1.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_1.c -map #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/nums_map_1.h b/gbdk-support/png2asset/testing/ref/nums_map_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_1.c -map + #ifndef METASPRITE_nums_map_1_H #define METASPRITE_nums_map_1_H diff --git a/gbdk-support/png2asset/testing/ref/nums_map_2.c b/gbdk-support/png2asset/testing/ref/nums_map_2.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_2.c -map -source_tileset /nums8x8_9_to_0.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/nums_map_2.h b/gbdk-support/png2asset/testing/ref/nums_map_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_2.c -map -source_tileset /nums8x8_9_to_0.png + #ifndef METASPRITE_nums_map_2_H #define METASPRITE_nums_map_2_H diff --git a/gbdk-support/png2asset/testing/ref/nums_map_3.c b/gbdk-support/png2asset/testing/ref/nums_map_3.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_3.c -map -entity_tileset /nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/nums_map_3.h b/gbdk-support/png2asset/testing/ref/nums_map_3.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_3.c -map -entity_tileset /nums8x8_30_to_39.png + #ifndef METASPRITE_nums_map_3_H #define METASPRITE_nums_map_3_H diff --git a/gbdk-support/png2asset/testing/ref/nums_map_4.c b/gbdk-support/png2asset/testing/ref/nums_map_4.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_4.c -map -source_tileset /nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/nums_map_4.h b/gbdk-support/png2asset/testing/ref/nums_map_4.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_4.c -map -source_tileset /nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png + #ifndef METASPRITE_nums_map_4_H #define METASPRITE_nums_map_4_H diff --git a/gbdk-support/png2asset/testing/ref/nums_map_5.c b/gbdk-support/png2asset/testing/ref/nums_map_5.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_5.c -map #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/nums_map_5.h b/gbdk-support/png2asset/testing/ref/nums_map_5.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_5.c -map + #ifndef METASPRITE_nums_map_5_H #define METASPRITE_nums_map_5_H diff --git a/gbdk-support/png2asset/testing/ref/nums_map_6.c b/gbdk-support/png2asset/testing/ref/nums_map_6.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_6.c -map -keep_duplicate_tiles -source_tileset /nums8x8_9_to_0.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/nums_map_6.h b/gbdk-support/png2asset/testing/ref/nums_map_6.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_6.c -map -keep_duplicate_tiles -source_tileset /nums8x8_9_to_0.png + #ifndef METASPRITE_nums_map_6_H #define METASPRITE_nums_map_6_H diff --git a/gbdk-support/png2asset/testing/ref/nums_map_7.c b/gbdk-support/png2asset/testing/ref/nums_map_7.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_7.c -map -keep_duplicate_tiles -entity_tileset /nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/nums_map_7.h b/gbdk-support/png2asset/testing/ref/nums_map_7.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_7.c -map -keep_duplicate_tiles -entity_tileset /nums8x8_30_to_39.png + #ifndef METASPRITE_nums_map_7_H #define METASPRITE_nums_map_7_H diff --git a/gbdk-support/png2asset/testing/ref/nums_map_8.c b/gbdk-support/png2asset/testing/ref/nums_map_8.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_8.c -map -keep_duplicate_tiles -source_tileset /nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/nums_map_8.h b/gbdk-support/png2asset/testing/ref/nums_map_8.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /nums_map_8.c -map -keep_duplicate_tiles -source_tileset /nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png + #ifndef METASPRITE_nums_map_8_H #define METASPRITE_nums_map_8_H diff --git a/gbdk-support/png2asset/testing/ref/remote.c b/gbdk-support/png2asset/testing/ref/remote.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /remote.png -c /remote.c -keep_palette_order -map -noflip #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/remote.h b/gbdk-support/png2asset/testing/ref/remote.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /remote.png -c /remote.c -keep_palette_order -map -noflip + #ifndef METASPRITE_remote_H #define METASPRITE_remote_H diff --git a/gbdk-support/png2asset/testing/ref/remote_down.c b/gbdk-support/png2asset/testing/ref/remote_down.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /remote_down.png -c /remote_down.c -keep_palette_order -map -noflip -source_tileset /remote.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/remote_down.h b/gbdk-support/png2asset/testing/ref/remote_down.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /remote_down.png -c /remote_down.c -keep_palette_order -map -noflip -source_tileset /remote.png + #ifndef METASPRITE_remote_down_H #define METASPRITE_remote_down_H diff --git a/gbdk-support/png2asset/testing/ref/sgb_border_1.c b/gbdk-support/png2asset/testing/ref/sgb_border_1.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sgb_border.png -c /sgb_border_1.c -map -bpp 4 -max_palettes 4 -pack_mode sgb -use_map_attributes #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/sgb_border_1.h b/gbdk-support/png2asset/testing/ref/sgb_border_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sgb_border.png -c /sgb_border_1.c -map -bpp 4 -max_palettes 4 -pack_mode sgb -use_map_attributes + #ifndef METASPRITE_sgb_border_1_H #define METASPRITE_sgb_border_1_H diff --git a/gbdk-support/png2asset/testing/ref/sgb_border_2.c b/gbdk-support/png2asset/testing/ref/sgb_border_2.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sgb_border.png -c /sgb_border_2.c -map -bpp 4 -max_palettes 4 -pack_mode sgb -use_map_attributes -no_palettes #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/sgb_border_2.h b/gbdk-support/png2asset/testing/ref/sgb_border_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sgb_border.png -c /sgb_border_2.c -map -bpp 4 -max_palettes 4 -pack_mode sgb -use_map_attributes -no_palettes + #ifndef METASPRITE_sgb_border_2_H #define METASPRITE_sgb_border_2_H diff --git a/gbdk-support/png2asset/testing/ref/sgb_border_multi_pal.c b/gbdk-support/png2asset/testing/ref/sgb_border_multi_pal.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sgb_border_multi_pal.png -c /sgb_border_multi_pal.c -map -bpp 4 -max_palettes 4 -pack_mode sgb -use_map_attributes #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/sgb_border_multi_pal.h b/gbdk-support/png2asset/testing/ref/sgb_border_multi_pal.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sgb_border_multi_pal.png -c /sgb_border_multi_pal.c -map -bpp 4 -max_palettes 4 -pack_mode sgb -use_map_attributes + #ifndef METASPRITE_sgb_border_multi_pal_H #define METASPRITE_sgb_border_multi_pal_H diff --git a/gbdk-support/png2asset/testing/ref/sprite_slon_1.c b/gbdk-support/png2asset/testing/ref/sprite_slon_1.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /sprite_slon_1.c -sh 48 -spr8x8 -noflip #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/sprite_slon_1.h b/gbdk-support/png2asset/testing/ref/sprite_slon_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /sprite_slon_1.c -sh 48 -spr8x8 -noflip + #ifndef METASPRITE_sprite_slon_1_H #define METASPRITE_sprite_slon_1_H diff --git a/gbdk-support/png2asset/testing/ref/sprite_slon_2.c b/gbdk-support/png2asset/testing/ref/sprite_slon_2.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /sprite_slon_2.c -sh 48 -spr8x16 -noflip #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/sprite_slon_2.h b/gbdk-support/png2asset/testing/ref/sprite_slon_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /sprite_slon_2.c -sh 48 -spr8x16 -noflip + #ifndef METASPRITE_sprite_slon_2_H #define METASPRITE_sprite_slon_2_H diff --git a/gbdk-support/png2asset/testing/ref/sprite_slon_3.c b/gbdk-support/png2asset/testing/ref/sprite_slon_3.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /sprite_slon_3.c -sh 48 -spr8x16 -noflip -metasprites_only #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/sprite_slon_3.h b/gbdk-support/png2asset/testing/ref/sprite_slon_3.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /sprite_slon_3.c -sh 48 -spr8x16 -noflip -metasprites_only + #ifndef METASPRITE_sprite_slon_3_H #define METASPRITE_sprite_slon_3_H diff --git a/gbdk-support/png2asset/testing/ref/sprite_slon_4.c b/gbdk-support/png2asset/testing/ref/sprite_slon_4.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /sprite_slon_4.c -sh 48 -spr8x16 -noflip -tiles_only #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/sprite_slon_4.h b/gbdk-support/png2asset/testing/ref/sprite_slon_4.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /sprite_slon_4.c -sh 48 -spr8x16 -noflip -tiles_only + #ifndef METASPRITE_sprite_slon_4_H #define METASPRITE_sprite_slon_4_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_1.c b/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_1.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /zgb_color_indexed_nums_map_1.c -map -use_map_attributes -keep_palette_order -use_structs #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_1.h b/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /zgb_color_indexed_nums_map_1.c -map -use_map_attributes -keep_palette_order -use_structs + #ifndef METASPRITE_zgb_color_indexed_nums_map_1_H #define METASPRITE_zgb_color_indexed_nums_map_1_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_2.c b/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_2.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /zgb_color_indexed_nums_map_2.c -map -use_map_attributes -keep_palette_order -use_structs -source_tileset /color_indexed_nums8x8_9_to_0.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_2.h b/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /zgb_color_indexed_nums_map_2.c -map -use_map_attributes -keep_palette_order -use_structs -source_tileset /color_indexed_nums8x8_9_to_0.png + #ifndef METASPRITE_zgb_color_indexed_nums_map_2_H #define METASPRITE_zgb_color_indexed_nums_map_2_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_3.c b/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_3.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /zgb_color_indexed_nums_map_3.c -map -use_map_attributes -keep_palette_order -use_structs -entity_tileset /color_indexed_nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_3.h b/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_3.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /zgb_color_indexed_nums_map_3.c -map -use_map_attributes -keep_palette_order -use_structs -entity_tileset /color_indexed_nums8x8_30_to_39.png + #ifndef METASPRITE_zgb_color_indexed_nums_map_3_H #define METASPRITE_zgb_color_indexed_nums_map_3_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_4.c b/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_4.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /zgb_color_indexed_nums_map_4.c -map -use_map_attributes -keep_palette_order -use_structs -source_tileset /color_indexed_nums8x8_9_to_0.png -entity_tileset /color_indexed_nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_4.h b/gbdk-support/png2asset/testing/ref/zgb_color_indexed_nums_map_4.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_indexed_nums8x8_map_0_to_39.png -o /zgb_color_indexed_nums_map_4.c -map -use_map_attributes -keep_palette_order -use_structs -source_tileset /color_indexed_nums8x8_9_to_0.png -entity_tileset /color_indexed_nums8x8_30_to_39.png + #ifndef METASPRITE_zgb_color_indexed_nums_map_4_H #define METASPRITE_zgb_color_indexed_nums_map_4_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_1.c b/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_1.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /zgb_color_nums_map_1.c -map -use_map_attributes -use_structs #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_1.h b/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /zgb_color_nums_map_1.c -map -use_map_attributes -use_structs + #ifndef METASPRITE_zgb_color_nums_map_1_H #define METASPRITE_zgb_color_nums_map_1_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_2.c b/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_2.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /zgb_color_nums_map_2.c -map -use_map_attributes -use_structs -source_tileset /color_nums8x8_9_to_0.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_2.h b/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /zgb_color_nums_map_2.c -map -use_map_attributes -use_structs -source_tileset /color_nums8x8_9_to_0.png + #ifndef METASPRITE_zgb_color_nums_map_2_H #define METASPRITE_zgb_color_nums_map_2_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_3.c b/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_3.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /zgb_color_nums_map_3.c -map -use_map_attributes -use_structs -entity_tileset /color_nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_3.h b/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_3.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /zgb_color_nums_map_3.c -map -use_map_attributes -use_structs -entity_tileset /color_nums8x8_30_to_39.png + #ifndef METASPRITE_zgb_color_nums_map_3_H #define METASPRITE_zgb_color_nums_map_3_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_4.c b/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_4.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /zgb_color_nums_map_4.c -map -use_map_attributes -use_structs -source_tileset /color_nums8x8_9_to_0.png -entity_tileset /color_nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_4.h b/gbdk-support/png2asset/testing/ref/zgb_color_nums_map_4.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /color_nums8x8_map_0_to_39.png -o /zgb_color_nums_map_4.c -map -use_map_attributes -use_structs -source_tileset /color_nums8x8_9_to_0.png -entity_tileset /color_nums8x8_30_to_39.png + #ifndef METASPRITE_zgb_color_nums_map_4_H #define METASPRITE_zgb_color_nums_map_4_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_largemap_1.c b/gbdk-support/png2asset/testing/ref/zgb_largemap_1.c @@ -1,6 +1,7 @@ #pragma bank 255 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /zgb_largemap_1.c -b 255 -map -use_structs -noflip #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_largemap_1.h b/gbdk-support/png2asset/testing/ref/zgb_largemap_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /zgb_largemap_1.c -b 255 -map -use_structs -noflip + #ifndef METASPRITE_zgb_largemap_1_H #define METASPRITE_zgb_largemap_1_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_largemap_2.c b/gbdk-support/png2asset/testing/ref/zgb_largemap_2.c @@ -1,6 +1,7 @@ #pragma bank 255 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /zgb_largemap_2.c -b 255 -map -use_structs -pack_mode sms -bpp 4 -keep_palette_order #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_largemap_2.h b/gbdk-support/png2asset/testing/ref/zgb_largemap_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /zgb_largemap_2.c -b 255 -map -use_structs -pack_mode sms -bpp 4 -keep_palette_order + #ifndef METASPRITE_zgb_largemap_2_H #define METASPRITE_zgb_largemap_2_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_largemap_3_attribs_smsgg.c b/gbdk-support/png2asset/testing/ref/zgb_largemap_3_attribs_smsgg.c @@ -1,6 +1,7 @@ #pragma bank 255 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /zgb_largemap_3_attribs_smsgg.c -b 255 -map -use_structs -pack_mode sms -bpp 4 -keep_palette_order -use_map_attributes #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_largemap_3_attribs_smsgg.h b/gbdk-support/png2asset/testing/ref/zgb_largemap_3_attribs_smsgg.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /zgb_largemap_3_attribs_smsgg.c -b 255 -map -use_structs -pack_mode sms -bpp 4 -keep_palette_order -use_map_attributes + #ifndef METASPRITE_zgb_largemap_3_attribs_smsgg_H #define METASPRITE_zgb_largemap_3_attribs_smsgg_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_largemap_4_attribs_gbc.c b/gbdk-support/png2asset/testing/ref/zgb_largemap_4_attribs_gbc.c @@ -1,6 +1,7 @@ #pragma bank 255 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /zgb_largemap_4_attribs_gbc.c -b 255 -map -use_structs -keep_palette_order -use_map_attributes #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_largemap_4_attribs_gbc.h b/gbdk-support/png2asset/testing/ref/zgb_largemap_4_attribs_gbc.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /largemap.png -c /zgb_largemap_4_attribs_gbc.c -b 255 -map -use_structs -keep_palette_order -use_map_attributes + #ifndef METASPRITE_zgb_largemap_4_attribs_gbc_H #define METASPRITE_zgb_largemap_4_attribs_gbc_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_1.c b/gbdk-support/png2asset/testing/ref/zgb_nums_map_1.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_1.c -map -use_structs #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_1.h b/gbdk-support/png2asset/testing/ref/zgb_nums_map_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_1.c -map -use_structs + #ifndef METASPRITE_zgb_nums_map_1_H #define METASPRITE_zgb_nums_map_1_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_2.c b/gbdk-support/png2asset/testing/ref/zgb_nums_map_2.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_2.c -map -use_structs -source_tileset /nums8x8_9_to_0.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_2.h b/gbdk-support/png2asset/testing/ref/zgb_nums_map_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_2.c -map -use_structs -source_tileset /nums8x8_9_to_0.png + #ifndef METASPRITE_zgb_nums_map_2_H #define METASPRITE_zgb_nums_map_2_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_3.c b/gbdk-support/png2asset/testing/ref/zgb_nums_map_3.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_3.c -map -use_structs -entity_tileset /nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_3.h b/gbdk-support/png2asset/testing/ref/zgb_nums_map_3.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_3.c -map -use_structs -entity_tileset /nums8x8_30_to_39.png + #ifndef METASPRITE_zgb_nums_map_3_H #define METASPRITE_zgb_nums_map_3_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_4.c b/gbdk-support/png2asset/testing/ref/zgb_nums_map_4.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_4.c -map -use_structs -source_tileset /nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_4.h b/gbdk-support/png2asset/testing/ref/zgb_nums_map_4.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_4.c -map -use_structs -source_tileset /nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png + #ifndef METASPRITE_zgb_nums_map_4_H #define METASPRITE_zgb_nums_map_4_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_5.c b/gbdk-support/png2asset/testing/ref/zgb_nums_map_5.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_5.c -map -use_structs #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_5.h b/gbdk-support/png2asset/testing/ref/zgb_nums_map_5.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_5.c -map -use_structs + #ifndef METASPRITE_zgb_nums_map_5_H #define METASPRITE_zgb_nums_map_5_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_6.c b/gbdk-support/png2asset/testing/ref/zgb_nums_map_6.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_6.c -map -use_structs -keep_duplicate_tiles -source_tileset /nums8x8_9_to_0.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_6.h b/gbdk-support/png2asset/testing/ref/zgb_nums_map_6.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_6.c -map -use_structs -keep_duplicate_tiles -source_tileset /nums8x8_9_to_0.png + #ifndef METASPRITE_zgb_nums_map_6_H #define METASPRITE_zgb_nums_map_6_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_7.c b/gbdk-support/png2asset/testing/ref/zgb_nums_map_7.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_7.c -map -use_structs -keep_duplicate_tiles -entity_tileset /nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_7.h b/gbdk-support/png2asset/testing/ref/zgb_nums_map_7.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_7.c -map -use_structs -keep_duplicate_tiles -entity_tileset /nums8x8_30_to_39.png + #ifndef METASPRITE_zgb_nums_map_7_H #define METASPRITE_zgb_nums_map_7_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_8.c b/gbdk-support/png2asset/testing/ref/zgb_nums_map_8.c @@ -1,4 +1,5 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_8.c -map -use_structs -keep_duplicate_tiles -source_tileset /nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_nums_map_8.h b/gbdk-support/png2asset/testing/ref/zgb_nums_map_8.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /nums8x8_map_0_to_39.png -o /zgb_nums_map_8.c -map -use_structs -keep_duplicate_tiles -source_tileset /nums8x8_9_to_0.png -entity_tileset /nums8x8_30_to_39.png + #ifndef METASPRITE_zgb_nums_map_8_H #define METASPRITE_zgb_nums_map_8_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_sgb_border_1.c b/gbdk-support/png2asset/testing/ref/zgb_sgb_border_1.c @@ -1,6 +1,7 @@ #pragma bank 255 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sgb_border.png -c /zgb_sgb_border_1.c -b 255 -map -bpp 4 -max_palettes 4 -use_map_attributes -use_structs -pack_mode sgb #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_sgb_border_1.h b/gbdk-support/png2asset/testing/ref/zgb_sgb_border_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sgb_border.png -c /zgb_sgb_border_1.c -b 255 -map -bpp 4 -max_palettes 4 -use_map_attributes -use_structs -pack_mode sgb + #ifndef METASPRITE_zgb_sgb_border_1_H #define METASPRITE_zgb_sgb_border_1_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_sprite_1.c b/gbdk-support/png2asset/testing/ref/zgb_sprite_1.c @@ -1,6 +1,7 @@ #pragma bank 255 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /zgb_sprite_1.c -use_structs -b 255 -spr8x16 -px 0 -py 0 -pw 32 -ph 24 -sw 64 -sh 48 #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_sprite_1.h b/gbdk-support/png2asset/testing/ref/zgb_sprite_1.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /zgb_sprite_1.c -use_structs -b 255 -spr8x16 -px 0 -py 0 -pw 32 -ph 24 -sw 64 -sh 48 + #ifndef METASPRITE_zgb_sprite_1_H #define METASPRITE_zgb_sprite_1_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_sprite_2.c b/gbdk-support/png2asset/testing/ref/zgb_sprite_2.c @@ -1,6 +1,7 @@ #pragma bank 255 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /zgb_sprite_2.c -use_structs -b 255 -spr8x16 -px 0 -py 0 -pw 32 -ph 24 -sw 64 -sh 48 -noflip -pack_mode sms -bpp 4 -keep_palette_order #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_sprite_2.h b/gbdk-support/png2asset/testing/ref/zgb_sprite_2.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sprite_slon.png -c /zgb_sprite_2.c -use_structs -b 255 -spr8x16 -px 0 -py 0 -pw 32 -ph 24 -sw 64 -sh 48 -noflip -pack_mode sms -bpp 4 -keep_palette_order + #ifndef METASPRITE_zgb_sprite_2_H #define METASPRITE_zgb_sprite_2_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_sushi_level07.c b/gbdk-support/png2asset/testing/ref/zgb_sushi_level07.c @@ -1,6 +1,7 @@ #pragma bank 255 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sushi_level07.png -c /zgb_sushi_level07.c -b 255 -map -noflip -pack_mode sms -bpp 4 -keep_palette_order -use_structs -keep_palette_order -rel_paths -keep_duplicate_tiles -source_tileset sushi_tiles.png -entity_tileset sushi_objects.png #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_sushi_level07.h b/gbdk-support/png2asset/testing/ref/zgb_sushi_level07.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sushi_level07.png -c /zgb_sushi_level07.c -b 255 -map -noflip -pack_mode sms -bpp 4 -keep_palette_order -use_structs -keep_palette_order -rel_paths -keep_duplicate_tiles -source_tileset sushi_tiles.png -entity_tileset sushi_objects.png + #ifndef METASPRITE_zgb_sushi_level07_H #define METASPRITE_zgb_sushi_level07_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_sushi_tiles.c b/gbdk-support/png2asset/testing/ref/zgb_sushi_tiles.c @@ -1,6 +1,7 @@ #pragma bank 255 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sushi_tiles.png -c /zgb_sushi_tiles.c -b 255 -map -noflip -pack_mode sms -bpp 4 -keep_palette_order -use_structs -keep_palette_order -keep_duplicate_tiles -tiles_only #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_sushi_tiles.h b/gbdk-support/png2asset/testing/ref/zgb_sushi_tiles.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sushi_tiles.png -c /zgb_sushi_tiles.c -b 255 -map -noflip -pack_mode sms -bpp 4 -keep_palette_order -use_structs -keep_palette_order -keep_duplicate_tiles -tiles_only + #ifndef METASPRITE_zgb_sushi_tiles_H #define METASPRITE_zgb_sushi_tiles_H diff --git a/gbdk-support/png2asset/testing/ref/zgb_sushi_tiles_lit.c b/gbdk-support/png2asset/testing/ref/zgb_sushi_tiles_lit.c @@ -1,6 +1,7 @@ #pragma constseg LIT_255 //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sushi_tiles.png -c /zgb_sushi_tiles_lit.c -b 255 -area LIT -map -noflip -pack_mode sms -bpp 4 -keep_palette_order -use_structs -keep_palette_order -keep_duplicate_tiles -tiles_only #include <stdint.h> #include <gbdk/platform.h> diff --git a/gbdk-support/png2asset/testing/ref/zgb_sushi_tiles_lit.h b/gbdk-support/png2asset/testing/ref/zgb_sushi_tiles_lit.h @@ -1,4 +1,6 @@ //AUTOGENERATED FILE FROM png2asset +// Conversion args: /sushi_tiles.png -c /zgb_sushi_tiles_lit.c -b 255 -area LIT -map -noflip -pack_mode sms -bpp 4 -keep_palette_order -use_structs -keep_palette_order -keep_duplicate_tiles -tiles_only + #ifndef METASPRITE_zgb_sushi_tiles_lit_H #define METASPRITE_zgb_sushi_tiles_lit_H diff --git a/licenses/LICENSE_ZX0 b/licenses/LICENSE_ZX0 @@ -0,0 +1,89 @@ +# ZX0 compression code in GBCOMPRESS + +zlib license +Copyright (c) 2021 Emmanuel Marty + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + + The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + + Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + + This notice may not be removed or altered from any source distribution. + +---- + +creative commons license +CC0 1.0 Universal + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + + Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. + + Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. + + Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. + + Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. + + +# ZX0 compression code in the GBDK Library + +Copyright (c) 2021, 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: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. 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. + +3. Neither the name of the copyright holder nor the names of its + contributors may 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.