From 7cff35368d26e87a0638e9be2d56642665af0ac0 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Wed, 6 May 2020 23:30:01 +0300 Subject: [PATCH] Port to C to remove the Python dep, remove leftovers --- BootROMs/pb12.c | 95 +++++++++++++ BootROMs/pb12.py | 68 ---------- BootROMs/pb8.c | 341 ----------------------------------------------- Makefile | 9 +- 4 files changed, 101 insertions(+), 412 deletions(-) create mode 100644 BootROMs/pb12.c delete mode 100644 BootROMs/pb12.py delete mode 100644 BootROMs/pb8.c diff --git a/BootROMs/pb12.c b/BootROMs/pb12.c new file mode 100644 index 00000000..878dd0d2 --- /dev/null +++ b/BootROMs/pb12.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#include + +void opts(uint8_t byte, uint8_t *options) +{ + *(options++) = byte | ((byte << 1) & 0xff); + *(options++) = byte & (byte << 1); + *(options++) = byte | ((byte >> 1) & 0xff); + *(options++) = byte & (byte >> 1); +} + +int main() +{ + static uint8_t source[0x4000]; + size_t size = read(STDIN_FILENO, &source, sizeof(source)); + unsigned pos = 0; + assert(size <= 0x4000); + while (size && source[size - 1] == 0) { + size--; + } + + uint8_t *literals = NULL; + size_t literals_size = 0; + unsigned bits = 0; + unsigned control = 0; + unsigned prev[2] = {-1, -1}; // Unsigned to allow "not set" values + + while (true) { + + uint8_t byte = 0; + if (pos == size){ + if (bits == 0) break; + } + else { + byte = source[pos++]; + } + + if (byte == prev[0] || byte == prev[1]) { + bits += 2; + control <<= 1; + control |= 1; + control <<= 1; + if (byte == prev[1]) { + control |= 1; + } + } + else { + bits += 2; + control <<= 2; + uint8_t options[4]; + opts(prev[1], options); + bool found = false; + for (unsigned i = 0; i < 4; i++) { + if (options[i] == byte) { + // 01 = modify + control |= 1; + + bits += 2; + control <<= 2; + control |= i; + found = true; + break; + } + } + if (!found) { + literals = realloc(literals, literals_size++); + literals[literals_size - 1] = byte; + } + } + + prev[0] = prev[1]; + prev[1] = byte; + if (bits >= 8) { + uint8_t outctl = control >> (bits - 8); + assert(outctl != 1); + write(STDOUT_FILENO, &outctl, 1); + write(STDOUT_FILENO, literals, literals_size); + bits -= 8; + control &= (1 << bits) - 1; + literals_size = 0; + } + } + uint8_t end_byte = 1; + write(STDOUT_FILENO, &end_byte, 1); + + if (literals) { + free(literals); + } + + return 0; +} diff --git a/BootROMs/pb12.py b/BootROMs/pb12.py deleted file mode 100644 index dc53d6b9..00000000 --- a/BootROMs/pb12.py +++ /dev/null @@ -1,68 +0,0 @@ -import sys - -def opts(byte): - # top bit: 0 = left, 1 = right - # bottom bit: 0 = or, 1 = and - if byte is None: return [] - return [ - byte | (byte << 1) & 0xff, - byte & (byte << 1), - byte | (byte >> 1) & 0xff, - byte & (byte >> 1), - ] - -def pb12(data): - data = iter(data) - - literals = bytearray() - bits = 0 - control = 0 - prev = [None, None] - gotta_end = False - - chunk = bytearray() - while True: - try: - byte = next(data) - except StopIteration: - if bits == 0: break - byte = 0 - chunk.append(byte) - - if byte in prev: - bits += 2 - control <<= 1 - control |= 1 - control <<= 1 - if prev[1] == byte: - control |= 1 # 10 = out[-2], 11 = out[-1] - else: - bits += 2 - control <<= 2 - options = opts(prev[1]) - if byte in options: - # 01 = modify - control |= 1 - - bits += 2 - control <<= 2 - control |= options.index(byte) - else: - # 00 = literal - literals.append(byte) - prev = [prev[1], byte] - if bits >= 8: - outctl = control >> (bits - 8) - assert outctl != 1 # that's the end byte - yield bytes([outctl]) + literals - bits -= 8 - control &= (1 << bits) - 1 - literals = bytearray() - chunk = bytearray() - yield b'\x01' - -_, infile, outfile = sys.argv -with open(infile, 'rb') as f: - data = f.read().rstrip(b'\x00') -with open(outfile, 'wb') as f: - f.writelines(pb12(data)) diff --git a/BootROMs/pb8.c b/BootROMs/pb8.c deleted file mode 100644 index 4ee4524e..00000000 --- a/BootROMs/pb8.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - -PB8 compressor and decompressor - -Copyright 2019 Damian Yerrick - -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. - -*/ - -#include -#include -#include -#include -#include -#include - -// For setting stdin/stdout to binary mode -#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) -#include -#define fd_isatty isatty -#elif defined (_WIN32) -#include -#include -#define fd_isatty _isatty -#endif - -/* - -; The logo is compressed using PB8, a form of RLE with unary-coded -; run lengths. Each block representing 8 bytes consists of a control -; byte, where each bit (MSB to LSB) is 0 for literal or 1 for repeat -; previous, followed by the literals in that block. - -SameBoyLogo_dst = $8080 -SameBoyLogo_length = (128 * 24) / 64 - -LoadTileset: - ld hl, SameBoyLogo - ld de, SameBoyLogo_dst - ld c, SameBoyLogo_length -.pb8BlockLoop: - ; Register map for PB8 decompression - ; HL: source address in boot ROM - ; DE: destination address in VRAM - ; A: Current literal value - ; B: Repeat bits, terminated by 1000... - ; C: Number of 8-byte blocks left in this block - ; Source address in HL lets the repeat bits go straight to B, - ; bypassing A and avoiding spilling registers to the stack. - ld b, [hl] - inc hl - - ; Shift a 1 into lower bit of shift value. Once this bit - ; reaches the carry, B becomes 0 and the byte is over - scf - rl b - -.pb8BitLoop: - ; If not a repeat, load a literal byte - jr c,.pb8Repeat - ld a, [hli] -.pb8Repeat: - ; Decompressed data uses colors 0 and 1, so write once, inc twice - ld [de], a - inc de - inc de - sla b - jr nz, .pb8BitLoop - - dec c - jr nz, .pb8BlockLoop - ret - -*/ - -/* Compressor and decompressor *************************************/ - -/** - * Compresses an input stream to PB8 data on an output stream. - * @param infp input stream - * @param outfp output stream - * @param blocklength size of an independent input block in bytes - * @return 0 for reaching infp end of file, or EOF for error - */ -int pb8(FILE *infp, FILE *outfp, size_t blocklength) -{ - blocklength >>= 3; // convert bytes to blocks - assert(blocklength > 0); - while (1) { - int last_byte = EOF; // value that never occurs in a file - for (size_t blkleft = blocklength; blkleft > 0; --blkleft) { - unsigned int control_byte = 0x0001; - unsigned char literals[8]; - size_t nliterals = 0; - while (control_byte < 0x100) { - int c = fgetc(infp); - if (c == EOF) break; - - control_byte <<= 1; - if (c == last_byte) { - control_byte |= 0x01; - } - else { - literals[nliterals++] = last_byte = c; - } - } - if (control_byte > 1) { - // Fill partial block with repeats - while (control_byte < 0x100) { - control_byte = (control_byte << 1) | 1; - } - - // Write control byte and check for write failure - int ok = fputc(control_byte & 0xFF, outfp); - if (ok == EOF) return EOF; - size_t ok2 = fwrite(literals, 1, nliterals, outfp); - if (ok2 < nliterals) return EOF; - } - - // If finished, return success or failure - if (ferror(infp) || ferror(outfp)) return EOF; - if (feof(infp)) return 0; - } // End 8-byte block - } // End packet, resetting last_byte -} - -/** - * Decompresses PB8 data on an input stream to an output stream. - * @param infp input stream - * @param outfp output stream - * @return 0 for reaching infp end of file, or EOF for error - */ -int unpb8(FILE *infp, FILE *outfp) -{ - int last_byte = 0; - while (1) { - int control_byte = fgetc(infp); - if (control_byte == EOF) { - return feof(infp) ? 0 : EOF; - } - control_byte &= 0xFF; - for (size_t bytesleft = 8; bytesleft > 0; --bytesleft) { - if (!(control_byte & 0x80)) { - last_byte = fgetc(infp); - if (last_byte == EOF) return EOF; // read error - } - control_byte <<= 1; - int ok = fputc(last_byte, outfp); - if (ok == EOF) return EOF; - } - } -} - -/* CLI frontend ****************************************************/ - -static inline void set_fd_binary(unsigned int fd) -{ -#ifdef _WIN32 - _setmode(fd, _O_BINARY); -#else - (void) fd; -#endif -} - -static const char *usage_msg = -"usage: pb8 [-d] [-l blocklength] [infile [outfile]]\n" -"Compresses a file using RLE with unary run and literal lengths.\n" -"\n" -"options:\n" -" -d decompress\n" -" -l blocklength allow RLE packets to span up to blocklength\n" -" input bytes (multiple of 8; default 8)\n" -" -h, -?, --help show this usage page\n" -" --version show copyright info\n" -"\n" -"If infile is - or missing, it is standard input.\n" -"If outfile is - or missing, it is standard output.\n" -"You cannot compress to or decompress from a terminal.\n" -; -static const char *version_msg = -"PB8 compressor (C version) v0.01\n" -"Copyright 2019 Damian Yerrick \n" -"This software is provided 'as-is', without any express or implied\n" -"warranty.\n" -; -static const char *toomanyfilenames_msg = -"pb8: too many filenames; try pb8 --help\n"; - -int main(int argc, char **argv) -{ - const char *infilename = NULL; - const char *outfilename = NULL; - bool decompress = false; - size_t blocklength = 8; - - for (int i = 1; i < argc; ++i) { - if (argv[i][0] == '-' && argv[i][1] != 0) { - if (!strcmp(argv[i], "--help")) { - fputs(usage_msg, stdout); - return 0; - } - if (!strcmp(argv[i], "--version")) { - fputs(version_msg, stdout); - return 0; - } - - // -t1 or -t 1 - int argtype = argv[i][1]; - switch (argtype) { - case 'h': - case '?': - fputs(usage_msg, stdout); - return 0; - - case 'd': - decompress = true; - break; - - case 'l': { - const char *argvalue = argv[i][2] ? argv[i] + 2 : argv[++i]; - const char *endptr = NULL; - - unsigned long tvalue = strtoul(argvalue, (char **)&endptr, 10); - if (endptr == argvalue || tvalue == 0 || tvalue > SIZE_MAX) { - fprintf(stderr, "pb8: block length %s not a positive integer\n", - argvalue); - return EXIT_FAILURE; - } - if (tvalue % 8 != 0) { - fprintf(stderr, "pb8: block length %s not a multiple of 8\n", - argvalue); - return EXIT_FAILURE; - } - blocklength = tvalue; - } break; - - default: - fprintf(stderr, "pb8: unknown option -%c\n", argtype); - return EXIT_FAILURE; - } - } - else if (!infilename) { - infilename = argv[i]; - } - else if (!outfilename) { - outfilename = argv[i]; - } - else { - fputs(toomanyfilenames_msg, stderr); - return EXIT_FAILURE; - } - } - if (infilename && !strcmp(infilename, "-")) { - infilename = NULL; - } - if (!infilename && decompress && fd_isatty(0)) { - fputs("pb8: cannot decompress from terminal; try redirecting stdin\n", - stderr); - return EXIT_FAILURE; - } - if (outfilename && !strcmp(outfilename, "-")) { - outfilename = NULL; - } - if (!outfilename && !decompress && fd_isatty(1)) { - fputs("pb8: cannot compress to terminal; try redirecting stdout or pb8 --help\n", - stderr); - return EXIT_FAILURE; - } - - FILE *infp = NULL; - if (infilename) { - infp = fopen(infilename, "rb"); - if (!infp) { - fprintf(stderr, "pb8: error opening %s ", infilename); - perror("for reading"); - return EXIT_FAILURE; - } - } - else { - infp = stdin; - set_fd_binary(0); - } - - FILE *outfp = NULL; - if (outfilename) { - outfp = fopen(outfilename, "wb"); - if (!outfp) { - fprintf(stderr, "pb8: error opening %s ", outfilename); - perror("for writing"); - fclose(infp); - return EXIT_FAILURE; - } - } - else { - outfp = stdout; - set_fd_binary(1); - } - - int compfailed = 0; - int has_ferror = 0; - if (decompress) { - compfailed = unpb8(infp, outfp); - } - else { - compfailed = pb8(infp, outfp, blocklength); - } - fflush(outfp); - if (ferror(infp)) { - fprintf(stderr, "pb8: error reading %s\n", - infilename ? infilename : ""); - has_ferror = EOF; - } - fclose(infp); - if (ferror(outfp)) { - fprintf(stderr, "pb8: error writing %s\n", - outfilename ? outfilename : ""); - has_ferror = EOF; - } - fclose(outfp); - - if (compfailed && !has_ferror) { - fputs("pb8: unknown compression failure\n", stderr); - } - - return (compfailed || has_ferror) ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/Makefile b/Makefile index 1f818628..78978e49 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ else EXESUFFIX:= endif -PB8_COMPRESS := build/pb8$(EXESUFFIX) +PB12_COMPRESS := build/pb12$(EXESUFFIX) ifeq ($(PLATFORM),Darwin) DEFAULT := cocoa @@ -386,8 +386,11 @@ $(OBJ)/%.2bpp: %.png -@$(MKDIR) -p $(dir $@) rgbgfx -h -u -o $@ $< -$(OBJ)/BootROMs/SameBoyLogo.pb12: $(OBJ)/BootROMs/SameBoyLogo.2bpp BootROMs/pb12.py - python3 BootROMs/pb12.py $< $@ +$(OBJ)/BootROMs/SameBoyLogo.pb12: $(OBJ)/BootROMs/SameBoyLogo.2bpp $(PB12_COMPRESS) + $(PB12_COMPRESS) < $< > $@ + +$(PB12_COMPRESS): BootROMs/pb12.c + $(CC) -Wall -Werror $< -o $@ $(BIN)/BootROMs/agb_boot.bin: BootROMs/cgb_boot.asm $(BIN)/BootROMs/cgb_boot_fast.bin: BootROMs/cgb_boot.asm