Skip to content

Commit 0044a3b

Browse files
authored
Latest version of PXA with important bug fixes
1 parent 9646632 commit 0044a3b

File tree

1 file changed

+154
-28
lines changed

1 file changed

+154
-28
lines changed

pxa_compress_snippets.c

Lines changed: 154 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/*
22
3-
pxa compression snippets for PICO-8 0.2.0 cartridge format
3+
pxa compression snippets for PICO-8 cartridge format (as of 0.2.4c)
44
55
author: joseph@lexaloffle.com
66
7-
Copyright (c) 2020 Lexaloffle Games LLP
7+
Copyright (c) 2020-22 Lexaloffle Games LLP
88
99
Permission is hereby granted, free of charge, to any person obtaining a copy
1010
of this software and associated documentation files (the "Software"), to deal
@@ -66,6 +66,21 @@ static int src_pos = 0;
6666
static uint8 *dest_buf = NULL;
6767
static uint8 *src_buf = NULL;
6868

69+
// 0.2.0j
70+
// encode / decode as an int
71+
static int get_write_pos()
72+
{
73+
int result = (dest_pos << 16) | (byte << 8) | bit;
74+
return result;
75+
}
76+
static void set_write_pos(int val)
77+
{
78+
bit = val & 0xff;
79+
byte = (val >> 8) & 0xff;
80+
dest_pos = (val >> 16) & 0x7fff;
81+
}
82+
83+
6984

7085
static int getbit()
7186
{
@@ -81,18 +96,18 @@ static int getbit()
8196
return ret;
8297
}
8398

84-
static void putbit(int bval)
99+
void putbit(int bval)
85100
{
86-
if (bval) byte |= bit;
101+
dest_buf[dest_pos] &= ~bit; // 0.2.0j: per-bit
102+
if (bval) dest_buf[dest_pos] |= bit;
87103

88-
dest_buf[dest_pos] = byte;
89104
bit <<= 1;
90105

91106
if (bit == 256)
92107
{
93108
bit = 1;
94-
byte = 0;
95109
dest_pos ++;
110+
byte = dest_buf[dest_pos]; // 0.2.0j: so that don't clobber existing bits (can overwrite at bit level)
96111
}
97112
}
98113

@@ -208,6 +223,9 @@ static int getnum()
208223

209224
val = getval(bits);
210225

226+
if (val == 0 && bits == 10)
227+
return -1; // raw block marker
228+
211229
return val;
212230
}
213231

@@ -228,7 +246,8 @@ static int pxa_find_repeatable_block(uint8 *dat, int pos, int data_len, int *blo
228246
int hash;
229247
int last_pos;
230248
int score, dist, bit_cost, best_score = -1;
231-
249+
int list_pos;
250+
232251
p = &dat[pos];
233252

234253
// block length can't be longer than remaining
@@ -241,12 +260,18 @@ static int pxa_find_repeatable_block(uint8 *dat, int pos, int data_len, int *blo
241260

242261
uint16 *list = hash_list[hash];
243262

244-
for (int list_pos = 0;
263+
/*
264+
for (list_pos = 0;
245265
list && list_pos < list[1] && // for each item of list
246266
(list[2+list_pos] < pos
247-
&& list[2+list_pos] >= pos - max_hist_len // where starting position in within range
267+
&& list[2+list_pos] >= pos - max_hist_len // where starting position in within range 0.2.0e: commented. **wroooong**. don't want to exit iter here.
248268
);
249269
list_pos++)
270+
*/
271+
272+
if (!list) return 0; // 0.2.0e: exit early
273+
for (list_pos = 0; list_pos < list[1] && list[2+list_pos] < pos; list_pos++) // 0.2.0e: can exit early if encounter future position (rest of list will also be)
274+
if (list[2+list_pos] >= pos - max_hist_len) // not out of range 0.2.0e: moved here -- still want to try rest of list
250275
{
251276
int pos0 = list[2 + list_pos];
252277

@@ -394,10 +419,12 @@ void pxa_build_hash_lookup(uint8 *in, int len)
394419
list[2 + list[1]] = i;
395420
list[1] ++;
396421
}
397-
398422
}
399423

400424

425+
#define BACKUP_VLIST_STATE() memcpy(literal_backup, literal, sizeof(literal)); memcpy(literal_pos_backup, literal_pos, sizeof(literal_pos));
426+
#define RESTORE_VLIST_STATE() memcpy(literal, literal_backup, sizeof(literal)); memcpy(literal_pos, literal_pos_backup, sizeof(literal_pos));
427+
401428

402429
int pxa_compress(uint8 *in_p, uint8 *out, int len)
403430
{
@@ -411,6 +438,17 @@ int pxa_compress(uint8 *in_p, uint8 *out, int len)
411438
int block_score, literal_score;
412439
int literal[256];
413440
int literal_pos[256];
441+
int literal_backup[256];
442+
int literal_pos_backup[256];
443+
444+
// 0.2.0j
445+
int raw_pos_src0 = 0;
446+
int raw_header_write_pos = 0;
447+
int raw_block_write_pos = 0;
448+
int raw_pos_src = 0;
449+
int raw_pos_dest = 0;
450+
int stored_last_segment_as_raw = 0;
451+
int raw_block_size = 0;
414452

415453

416454
init_literals_state(literal, literal_pos);
@@ -448,7 +486,15 @@ int pxa_compress(uint8 *in_p, uint8 *out, int len)
448486
num_blocks = 0;
449487
num_literals = 0;
450488
num_blocks_large = 0;
451-
489+
490+
// start looking for raw blocks
491+
raw_pos_dest = dest_pos;
492+
raw_pos_src = raw_pos_src0 = pos;
493+
raw_header_write_pos = get_write_pos();
494+
raw_block_write_pos = get_write_pos();
495+
BACKUP_VLIST_STATE();
496+
497+
452498
while (pos < len)
453499
{
454500
// either copy or literal
@@ -486,7 +532,8 @@ int pxa_compress(uint8 *in_p, uint8 *out, int len)
486532
if (block_len >= PXA_MIN_BLOCK_LEN && block_score > literal_score)
487533
if (block_score < 128) // 25% faster, only slight drop in compression ratio (lost avg 3.6 bytes across 5 carts)
488534
{
489-
for (int ii =1; ii < 3; ii++)
535+
int ii;
536+
for (ii =1; ii < 3; ii++)
490537
{
491538
int block_offset2=0;
492539
int block_score2=0;
@@ -576,15 +623,79 @@ int pxa_compress(uint8 *in_p, uint8 *out, int len)
576623
hash = MINI_HASH(in, i);
577624
found[hash] = i;
578625
}
626+
627+
// 0.2.0j: if last 32 bytes (or remaining end of input) written have a ratio worse than ~1.0, rewrite as a raw block instead
628+
629+
if (dest_pos - raw_pos_dest >= 32 || pos == len)
630+
{
631+
int compressed_size = dest_pos - raw_pos_dest;
632+
int raw_size = pos - raw_pos_src;
633+
int margin = raw_pos_src0 == raw_pos_src ? 3 : 0; // 3 for first section (header + null terminator), 0 for appended
634+
635+
// rewrite as raw block?
636+
if (compressed_size > raw_size + margin)
637+
{
638+
if (stored_last_segment_as_raw == 0) // write header
639+
{
640+
// write header marker 010 00000 00000
641+
raw_block_size = raw_size;
642+
raw_header_write_pos = raw_block_write_pos;
643+
set_write_pos(raw_header_write_pos);
644+
putbit(0); putbit(1); putbit(0); putval(0, 10);
645+
}
646+
else
647+
{
648+
// append
649+
set_write_pos(raw_block_write_pos);
650+
dest_pos--; // overwrite previous null terminator
651+
}
652+
653+
// write raw data (not aligned)
654+
int k = 0;
655+
for (k = 0; k < raw_size; k++)
656+
putval(in[raw_pos_src + k], 8);
657+
putval(0,8); // null terminator
658+
659+
stored_last_segment_as_raw = 1;
660+
RESTORE_VLIST_STATE();
661+
}
662+
else{
663+
// leave as-is; reset start position of next possible raw block
664+
stored_last_segment_as_raw = 0;
665+
raw_pos_src0 = pos;
666+
BACKUP_VLIST_STATE();
667+
}
668+
669+
raw_pos_dest = dest_pos;
670+
raw_pos_src = pos;
671+
raw_block_write_pos = get_write_pos();
672+
}
673+
579674
}
580675

581676
codo_free(modified_code);
582677

583-
int bytes_written = (bit == 0x1 ? dest_pos : dest_pos + 1);
678+
// advance to next byte (and zero any junk)
679+
while (bit != 1)
680+
putbit(0);
681+
682+
int bytes_written = dest_pos;
584683

585684
dest_buf[6] = bytes_written / 256;
586685
dest_buf[7] = bytes_written % 256;
587686

687+
688+
// 0.2.0e: compressed is larger than input -> just return input (same as pxc)
689+
// for storing binary data -- perhaps cart is mostly data w/ tiny stub
690+
// otherwise, storing binary string compresses to around 1.25 (see /pxa/gen_rnd.p8)
691+
if (bytes_written > len)
692+
{
693+
// 0.2.0j: fixed: was in (which now points to deallocated memory. discovered because oversized-cart get_cart_hash was failing!)
694+
// would also cause small, or data-heavy .png file save/load to fail
695+
memcpy(out, in_p, len);
696+
return len;
697+
}
698+
588699
return bytes_written;
589700
}
590701

@@ -627,19 +738,33 @@ int pxa_decompress(uint8 *in_p, uint8 *out_p, int max_len)
627738
// block
628739

629740
int block_offset = getnum() + 1;
630-
int block_len = getchain(BLOCK_LEN_CHAIN_BITS, 100000) + PXA_MIN_BLOCK_LEN;
631741

632-
// copy // don't just memcpy because might be copying self for repeating pattern
633-
while (block_len > 0){
634-
out_p[dest_pos] = out_p[dest_pos - block_offset];
635-
dest_pos++;
636-
block_len--;
742+
if (block_offset == 0)
743+
{
744+
// 0.2.0j: raw block
745+
while (dest_pos < raw_len)
746+
{
747+
out_p[dest_pos] = getval(8);
748+
if (out_p[dest_pos] == 0) // found end -- don't advance dest_pos
749+
break;
750+
dest_pos ++;
751+
}
637752
}
753+
else
754+
{
755+
int block_len = getchain(BLOCK_LEN_CHAIN_BITS, 100000) + PXA_MIN_BLOCK_LEN;
638756

639-
// safety: null terminator. to do: just do at end
640-
if (dest_pos < max_len-1)
641-
out_p[dest_pos] = 0;
757+
// copy // don't just memcpy because might be copying self for repeating pattern
758+
while (block_len > 0){
759+
out_p[dest_pos] = out_p[dest_pos - block_offset];
760+
dest_pos++;
761+
block_len--;
762+
}
642763

764+
// safety: null terminator. to do: just do at end
765+
if (dest_pos < max_len-1)
766+
out_p[dest_pos] = 0;
767+
}
643768
}else
644769
{
645770
// literal
@@ -666,7 +791,8 @@ int pxa_decompress(uint8 *in_p, uint8 *out_p, int max_len)
666791
dest_pos++;
667792
out_p[dest_pos] = 0;
668793

669-
for (int i = lpos; i > 0; i--)
794+
int i;
795+
for (i = lpos; i > 0; i--)
670796
{
671797
literal[i] = literal[i-1];
672798
literal_pos[literal[i]] ++;
@@ -687,16 +813,16 @@ int is_compressed_format_header(uint8 *dat)
687813
return 0;
688814
}
689815

690-
816+
// max_len should be 0x10000 (64k max code size)
817+
// out_p should allocate 0x10001 (includes null terminator)
691818
int pico8_code_section_decompress(uint8 *in_p, uint8 *out_p, int max_len)
692819
{
693-
//if (is_compressed_format_header(in_p) == 1) return pxc_decompress_mini(in_p, out_p, max_len);
694-
if (is_compressed_format_header(in_p) == 2) return pxa_decompress (in_p, out_p, max_len);
820+
if (is_compressed_format_header(in_p) == 0) { memcpy(out_p, in_p, 0x3d00); out_p[0x3d00] = '\0'; return 0; } // legacy: no header -> is raw text
821+
if (is_compressed_format_header(in_p) == 1) return decompress_mini(in_p, out_p, max_len);
822+
if (is_compressed_format_header(in_p) == 2) return pxa_decompress (in_p, out_p, max_len);
695823
return 0;
696824
}
697825

698826

699827

700828

701-
702-

0 commit comments

Comments
 (0)