#include "Compression.hh" #include #include #include #include #include #include using namespace std; struct prs_compress_ctx { unsigned char bitpos; std::string forward_log; std::string output; prs_compress_ctx() : bitpos(0) { } string finish() { this->put_control_bit(0); this->put_control_bit(1); if (this->bitpos != 0) { this->forward_log[0] = ((this->forward_log[0] << this->bitpos) >> 8); } this->put_static_data(0); this->put_static_data(0); this->output += this->forward_log; this->forward_log.clear(); return this->output; } void put_control_bit_nosave(bool bit) { this->forward_log[0] = this->forward_log[0] >> 1; this->forward_log[0] |= ((!!bit) << 7); this->bitpos++; } void put_control_save() { if (this->bitpos >= 8) { this->bitpos = 0; this->output += this->forward_log; this->forward_log.resize(1); this->forward_log[0] = 0; } } void put_control_bit(bool bit) { this->put_control_bit_nosave(bit); this->put_control_save(); } void put_static_data(uint8_t data) { this->forward_log += static_cast(data); } void raw_byte(uint8_t value) { this->put_control_bit_nosave(1); this->put_static_data(value); this->put_control_save(); } void short_copy(ssize_t offset, uint8_t size) { size -= 2; this->put_control_bit(0); this->put_control_bit(0); this->put_control_bit((size >> 1) & 1); this->put_control_bit_nosave(size & 1); this->put_static_data(offset & 0xFF); this->put_control_save(); } void long_copy(ssize_t offset, uint8_t size) { if (size <= 9) { this->put_control_bit(0); this->put_control_bit_nosave(1); this->put_static_data(((offset << 3) & 0xF8) | ((size - 2) & 0x07)); this->put_static_data((offset >> 5) & 0xFF); this->put_control_save(); } else { this->put_control_bit(0); this->put_control_bit_nosave(1); this->put_static_data((offset << 3) & 0xF8); this->put_static_data((offset >> 5) & 0xFF); this->put_static_data(size - 1); this->put_control_save(); } } void copy(ssize_t offset, uint8_t size) { if ((offset > -0x100) && (size <= 5)) { this->short_copy(offset, size); } else { this->long_copy(offset, size); } } }; string prs_compress(const string& data) { prs_compress_ctx pc; ssize_t read_offset = 0; while (read_offset < static_cast(data.size())) { // look for a chunk of data in history matching what's at the current offset ssize_t best_offset = 0; size_t best_size = 0; for (ssize_t this_offset = -3; (this_offset + data.size() >= 0) && (this_offset > -0x1FF0) && (best_size < 255); this_offset--) { // for this offset, expand the match as much as possible size_t this_size = 1; while ((this_size < 0x100) && // max copy size is 255 bytes ((this_offset + this_size) < 0) && // don't copy past the read offset (this_size <= data.size() - read_offset) && // don't copy past the end !memcmp(data.data() + read_offset + this_offset, data.data() + read_offset, this_size)) { this_size++; } this_size--; if (this_size > best_size) { best_offset = this_offset; best_size = this_size; } } // if there are no good matches, write the byte directly if (best_size < 3) { pc.raw_byte(data[read_offset]); read_offset++; } else { pc.copy(best_offset, best_size); read_offset += best_size; } } return pc.finish(); } static int16_t get_u8_or_eof(StringReader& r) { return r.eof() ? -1 : r.get_u8(); } string prs_decompress(const string& data, size_t max_size) { string output; StringReader r(data.data(), data.size()); int32_t r3, r5; int bitpos = 9; int16_t currentbyte; // int16_t because it can be -1 when EOF occurs int flag; int offset; unsigned long x, t; currentbyte = get_u8_or_eof(r); if (currentbyte == EOF) { return output; } for (;;) { bitpos--; if (bitpos == 0) { currentbyte = get_u8_or_eof(r); if (currentbyte == EOF) { return output; } bitpos = 8; } flag = currentbyte & 1; currentbyte = currentbyte >> 1; if (flag) { int ch = get_u8_or_eof(r); if (ch == EOF) { return output; } output += static_cast(ch); if (max_size && (output.size() > max_size)) { throw runtime_error("maximum output size exceeded"); } continue; } bitpos--; if (bitpos == 0) { currentbyte = get_u8_or_eof(r); if (currentbyte == EOF) { return output; } bitpos = 8; } flag = currentbyte & 1; currentbyte = currentbyte >> 1; if (flag) { r3 = get_u8_or_eof(r); if (r3 == EOF) { return output; } int high_byte = get_u8_or_eof(r); if (high_byte == EOF) { return output; } offset = ((high_byte & 0xFF) << 8) | (r3 & 0xFF); if (offset == 0) { return output; } r3 = r3 & 0x00000007; r5 = (offset >> 3) | 0xFFFFE000; if (r3 == 0) { flag = 0; r3 = get_u8_or_eof(r); if (r3 == EOF) { return output; } r3 = (r3 & 0xFF) + 1; } else { r3 += 2; } } else { r3 = 0; for (x = 0; x < 2; x++) { bitpos--; if (bitpos == 0) { currentbyte = get_u8_or_eof(r); if (currentbyte == EOF) { return output; } bitpos = 8; } flag = currentbyte & 1; currentbyte = currentbyte >> 1; offset = r3 << 1; r3 = offset | flag; } offset = get_u8_or_eof(r); if (offset == EOF) { return output; } r3 += 2; r5 = offset | 0xFFFFFF00; } if (r3 == 0) { continue; } t = r3; for (x = 0; x < t; x++) { output += output.at(output.size() + r5); if (max_size && (output.size() > max_size)) { throw runtime_error("maximum output size exceeded"); } } } } size_t prs_decompress_size(const string& data, size_t max_size) { size_t output_size = 0; StringReader r(data.data(), data.size()); int32_t r3; int bitpos = 9; int16_t currentbyte; // int16_t because it can be -1 when EOF occurs int flag; int offset; unsigned long x; currentbyte = get_u8_or_eof(r); if (currentbyte == EOF) { return output_size; } for (;;) { bitpos--; if (bitpos == 0) { currentbyte = get_u8_or_eof(r); if (currentbyte == EOF) { return output_size; } bitpos = 8; } flag = currentbyte & 1; currentbyte = currentbyte >> 1; if (flag) { int ch = get_u8_or_eof(r); if (ch == EOF) { return output_size; } output_size++; if (max_size && (output_size > max_size)) { throw runtime_error("maximum output size exceeded"); } continue; } bitpos--; if (bitpos == 0) { currentbyte = get_u8_or_eof(r); if (currentbyte == EOF) { return output_size; } bitpos = 8; } flag = currentbyte & 1; currentbyte = currentbyte >> 1; if (flag) { r3 = get_u8_or_eof(r); if (r3 == EOF) { return output_size; } int high_byte = get_u8_or_eof(r); if (high_byte == EOF) { return output_size; } offset = ((high_byte & 0xFF) << 8) | (r3 & 0xFF); if (offset == 0) { return output_size; } r3 = r3 & 0x00000007; if (r3 == 0) { flag = 0; r3 = get_u8_or_eof(r); if (r3 == EOF) { return output_size; } r3 = (r3 & 0xFF) + 1; } else { r3 += 2; } } else { r3 = 0; for (x = 0; x < 2; x++) { bitpos--; if (bitpos == 0) { currentbyte = get_u8_or_eof(r); if (currentbyte == EOF) { return output_size; } bitpos = 8; } flag = currentbyte & 1; currentbyte = currentbyte >> 1; offset = r3 << 1; r3 = offset | flag; } offset = get_u8_or_eof(r); if (offset == EOF) { return output_size; } r3 += 2; } if (r3 == 0) { continue; } output_size += r3; if (max_size && (output_size > max_size)) { throw runtime_error("maximum output size exceeded"); } } }