mirror of https://github.com/bsnes-emu/bsnes.git
Port to C to remove the Python dep, remove leftovers
This commit is contained in:
parent
184743637e
commit
7cff35368d
|
@ -0,0 +1,95 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
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;
|
||||
}
|
|
@ -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))
|
341
BootROMs/pb8.c
341
BootROMs/pb8.c
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
// For setting stdin/stdout to binary mode
|
||||
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
|
||||
#include <unistd.h>
|
||||
#define fd_isatty isatty
|
||||
#elif defined (_WIN32)
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#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 <https://pineight.com/contact/>\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 : "<stdin>");
|
||||
has_ferror = EOF;
|
||||
}
|
||||
fclose(infp);
|
||||
if (ferror(outfp)) {
|
||||
fprintf(stderr, "pb8: error writing %s\n",
|
||||
outfilename ? outfilename : "<stdout>");
|
||||
has_ferror = EOF;
|
||||
}
|
||||
fclose(outfp);
|
||||
|
||||
if (compfailed && !has_ferror) {
|
||||
fputs("pb8: unknown compression failure\n", stderr);
|
||||
}
|
||||
|
||||
return (compfailed || has_ferror) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
9
Makefile
9
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
|
||||
|
|
Loading…
Reference in New Issue