8#include "absl/status/status.h"
9#include "absl/status/statusor.h"
10#include "absl/strings/str_format.h"
15#define DEBUG_LOG(msg) std::cout << msg << std::endl
21 int const oldsize,
int*
const size,
26 constexpr int kMaxCommandLength = 1024;
29 std::vector<uint8_t> b2(std::max(0x1000, oldsize * 2));
31 int i, j, k, l, m = 0, n, o = 0, bd = 0, p, q = 0, r;
34 auto flush_literals = [&]() {
38 b2[bd++] = (
unsigned char)(224 + (q >> 8));
41 b2[bd++] = (
unsigned char)q;
44 memcpy(b2.data() + bd, src + i - q, q);
50 for (i = 0; i < oldsize;) {
58 for (j = 0; j < i - 1; j++) {
62 m = std::min(oldsize - i, kMaxCommandLength);
64 for (n = 0; n < m; n++)
65 if (src[n + j] != src[n + i])
73 for (n = i + 1; n < oldsize; n++) {
84 m = i + 1 < oldsize ? src[i + 1] : 0;
86 for (n = i + 2; n < oldsize; n++) {
92 if (n >= oldsize || src[n] != m)
103 for (n = 1; n < m; n++)
104 if (src[i + n] != l + n)
114 if (k > 3 + r && k > n + (p & 1))
119 if (q == kMaxCommandLength) {
127 n = std::min(n, kMaxCommandLength);
132 b2[bd++] = (
unsigned char)(224 + (n >> 8) + (p << 2));
133 b2[bd++] = (
unsigned char)n;
135 b2[bd++] = (
unsigned char)((p << 5) + n);
140 b2[bd++] = (
unsigned char)l;
144 b2[bd++] = (
unsigned char)l;
145 b2[bd++] = (
unsigned char)m;
151 b2[bd++] = (
unsigned char)(o >> 8);
152 b2[bd++] = (
unsigned char)o;
154 b2[bd++] = (
unsigned char)o;
155 b2[bd++] = (
unsigned char)(o >> 8);
175 int const p_big_endian,
176 size_t max_src_size) {
177 std::vector<uint8_t> b2(1024);
178 const uint8_t*
const src_start = src;
180 int bd = 0, bs = 1024;
187 auto decompress_loop = [&]() {
190 if (max_src_size !=
static_cast<size_t>(-1) &&
191 (size_t)(src - src_start) >= max_src_size) {
193 <<
"HyruleMagicDecompress: Reached end of buffer unexpectedly."
214 c = ((a & 0x0003) << 8);
217 if (max_src_size !=
static_cast<size_t>(-1) &&
218 (size_t)(src - src_start) >= max_src_size) {
219 std::cerr <<
"HyruleMagicDecompress: Reached end of buffer reading "
227 c = (uint16_t)(a & 31);
231 if ((bd + c) > (bs - 512)) {
235 if (bs > 1024 * 1024 * 16) {
236 std::cerr <<
"HyruleMagicDecompress: Excessive allocation detected."
252 if (max_src_size !=
static_cast<size_t>(-1) &&
253 (size_t)(src - src_start + c) > max_src_size) {
254 std::cerr <<
"HyruleMagicDecompress: Raw copy exceeds buffer."
260 memcpy(b2.data() + bd, src, c);
274 if (max_src_size !=
static_cast<size_t>(-1) &&
275 (
size_t)(src - src_start) >= max_src_size) {
276 std::cerr <<
"HyruleMagicDecompress: RLE copy exceeds buffer."
282 memset(b2.data() + bd, *(src++), c);
294 if (max_src_size !=
static_cast<size_t>(-1) &&
295 (
size_t)(src - src_start + 2) > max_src_size) {
297 <<
"HyruleMagicDecompress: RLE 16-bit copy exceeds buffer."
324 if (max_src_size !=
static_cast<size_t>(-1) &&
325 (size_t)(src - src_start) >= max_src_size) {
326 std::cerr <<
"HyruleMagicDecompress: Inc copy exceeds buffer."
345 if (max_src_size !=
static_cast<size_t>(-1) &&
346 (size_t)(src - src_start + 2) > max_src_size) {
347 std::cerr <<
"HyruleMagicDecompress: LZ copy exceeds buffer."
353 d = (*src << 8) + src[1];
359 if (d >=
static_cast<unsigned short>(bs)) {
360 std::cerr <<
"HyruleMagicDecompress: LZ ref out of bounds."
385 std::cout <<
"Command: " << std::to_string(piece->command) <<
"\n";
386 std::cout <<
"Command Length: " << piece->length <<
"\n";
387 std::cout <<
"Argument: ";
388 auto arg_size = piece->argument.size();
389 for (
int i = 0; i < arg_size; ++i) {
390 printf(
"%02X ", piece->argument.at(i));
392 std::cout <<
"\nArgument Length: " << piece->argument_length <<
"\n";
396 auto compressed_chain = chain_head->next;
397 while (compressed_chain !=
nullptr) {
398 std::cout <<
"- Compression Piece -\n";
400 compressed_chain = compressed_chain->next;
406 const unsigned int last_pos) {
407 unsigned int pos = src_data_pos;
408 char byte_to_repeat = rom_data[pos];
409 while (pos <= last_pos && rom_data[pos] == byte_to_repeat) {
418 const unsigned int last_pos) {
419 if (src_data_pos + 2 <= last_pos &&
420 rom_data[src_data_pos] != rom_data[src_data_pos + 1]) {
421 unsigned int pos = src_data_pos;
422 char byte1 = rom_data[pos];
423 char byte2 = rom_data[pos + 1];
426 while (pos + 1 <= last_pos) {
427 if (rom_data[pos] == byte1 && rom_data[pos + 1] == byte2)
440 const unsigned int last_pos) {
441 unsigned int pos = src_data_pos;
442 char byte = rom_data[pos];
446 while (pos <= last_pos &&
byte == rom_data[pos]) {
456 const unsigned int last_pos,
unsigned int start) {
457 if (src_data_pos != start) {
458 unsigned int searching_pos = start;
459 unsigned int current_pos_u = src_data_pos;
460 unsigned int copied_size = 0;
461 unsigned int search_start = start;
463 while (searching_pos < src_data_pos && current_pos_u <= last_pos) {
464 while (rom_data[current_pos_u] != rom_data[searching_pos] &&
465 searching_pos < src_data_pos)
467 search_start = searching_pos;
468 while (current_pos_u <= last_pos &&
469 rom_data[current_pos_u] == rom_data[searching_pos] &&
470 searching_pos < src_data_pos) {
476 search_start -= start;
477 printf(
"- Found repeat of %d at %d\n", copied_size, search_start);
482 current_pos_u = src_data_pos;
492 uint& cmd_with_max) {
493 for (
unsigned int cmd_i = 1; cmd_i < 5; cmd_i++) {
494 unsigned int cmd_size_taken = data_size_taken[cmd_i];
499 if (cmd_size_taken > max_win && cmd_size_taken > cmd_size[cmd_i] &&
501 printf(
"==> C:%d / S:%d\n", cmd_i, cmd_size_taken);
502 cmd_with_max = cmd_i;
503 max_win = cmd_size_taken;
512 uint& src_data_pos,
uint& comp_accumulator,
513 uint& cmd_with_max,
uint& max_win) {
514 printf(
"- Ok we get a gain from %d\n", cmd_with_max);
516 buffer.push_back(cmd_args[cmd_with_max][0]);
517 if (cmd_size[cmd_with_max] == 2) {
518 buffer.push_back(cmd_args[cmd_with_max][1]);
521 auto new_comp_piece = std::make_shared<CompressionPiece>(
522 cmd_with_max, max_win, buffer, cmd_size[cmd_with_max]);
525 if (comp_accumulator != 0) {
526 std::string copy_buff;
527 copy_buff.resize(comp_accumulator);
528 for (
int i = 0; i < comp_accumulator; ++i) {
529 copy_buff[i] = rom_data[i + src_data_pos - comp_accumulator];
531 auto copy_chunk = std::make_shared<CompressionPiece>(
533 compressed_chain->next = copy_chunk;
534 compressed_chain = copy_chunk;
536 compressed_chain->next = new_comp_piece;
537 compressed_chain = new_comp_piece;
539 src_data_pos += max_win;
540 comp_accumulator = 0;
546 while (src_pos + i < last_pos && data[src_pos] == data[src_pos + i]) {
555 if (src_pos + 2 <= last_pos && data[src_pos] != data[src_pos + 1]) {
556 unsigned int pos = src_pos;
557 char byte1 = data[pos];
558 char byte2 = data[pos + 1];
561 while (pos + 1 <= last_pos) {
562 if (data[pos] == byte1 && data[pos + 1] == byte2)
575 unsigned int pos = src_data_pos;
576 char byte = rom_data[pos];
580 while (pos <= last_pos &&
byte == rom_data[pos]) {
589 const unsigned int last_pos,
unsigned int start,
591 if (src_data_pos != start) {
592 unsigned int searching_pos = start;
593 unsigned int current_pos_u = src_data_pos;
594 unsigned int copied_size = 0;
595 unsigned int search_start = start;
597 while (searching_pos < src_data_pos && current_pos_u <= last_pos) {
598 while (rom_data[current_pos_u] != rom_data[searching_pos] &&
599 searching_pos < src_data_pos)
601 search_start = searching_pos;
602 while (current_pos_u <= last_pos &&
603 rom_data[current_pos_u] == rom_data[searching_pos] &&
604 searching_pos < src_data_pos) {
610 search_start -= start;
611 printf(
"- Found repeat of %d at %d\n", copied_size, search_start);
616 current_pos_u = src_data_pos;
627 uint& cmd_with_max) {
628 for (
unsigned int cmd_i = 1; cmd_i < 5; cmd_i++) {
629 unsigned int cmd_size_taken = cmd.
data_size[cmd_i];
633 if (cmd_size_taken > max_win && cmd_size_taken >
kCommandSizes[cmd_i] &&
635 printf(
"==> C:%d / S:%d\n", cmd_i, cmd_size_taken);
636 cmd_with_max = cmd_i;
637 max_win = cmd_size_taken;
645 uint& src_data_pos,
uint& comp_accumulator,
646 uint& cmd_with_max,
uint& max_win) {
647 printf(
"- Ok we get a gain from %d\n", cmd_with_max);
649 buffer.push_back(cmd.
arguments[cmd_with_max][0]);
650 if (cmd.
cmd_size[cmd_with_max] == 2) {
651 buffer.push_back(cmd.
arguments[cmd_with_max][1]);
654 auto new_comp_piece = std::make_shared<CompressionPiece>(
655 cmd_with_max, max_win, buffer, cmd.
cmd_size[cmd_with_max]);
658 if (comp_accumulator != 0) {
659 std::string copy_buff;
660 copy_buff.resize(comp_accumulator);
661 for (
int i = 0; i < comp_accumulator; ++i) {
662 copy_buff[i] = rom_data[i + src_data_pos - comp_accumulator];
664 auto copy_chunk = std::make_shared<CompressionPiece>(
666 compressed_chain->next = copy_chunk;
667 compressed_chain = copy_chunk;
669 compressed_chain->next = new_comp_piece;
670 compressed_chain = new_comp_piece;
672 src_data_pos += max_win;
673 comp_accumulator = 0;
679 uint& uncompressed_data_size,
uint& best_command,
uint& best_command_gain) {
680 std::cout <<
"- Identified a gain from command: " << best_command
684 std::string argument_buffer;
685 argument_buffer.push_back(command.
arguments[best_command][0]);
686 if (command.
cmd_size[best_command] == 2) {
687 argument_buffer.push_back(command.
arguments[best_command][1]);
691 auto new_compression_piece = std::make_shared<CompressionPiece>(
692 best_command, best_command_gain, argument_buffer,
698 if (uncompressed_data_size != 0) {
699 std::string copy_buffer(uncompressed_data_size, 0);
700 for (
int i = 0; i < uncompressed_data_size; ++i) {
702 rom_data[i + source_data_position - uncompressed_data_size];
704 auto direct_copy_piece = std::make_shared<CompressionPiece>(
706 uncompressed_data_size);
709 compressed_chain->next = direct_copy_piece;
710 compressed_chain = direct_copy_piece;
714 compressed_chain->next = new_compression_piece;
715 compressed_chain = new_compression_piece;
719 source_data_position += best_command_gain;
720 uncompressed_data_size = 0;
729 switch (piece->command) {
732 new_piece = std::make_shared<CompressionPiece>(
733 piece->command, length_left, piece->argument, piece->argument_length);
736 new_piece = std::make_shared<CompressionPiece>(
737 piece->command, length_left, piece->argument, piece->argument_length);
738 new_piece->argument[0] =
744 new_piece = std::make_shared<CompressionPiece>(
745 piece->command, length_left, empty, length_left);
747 for (
int i = 0; i < length_left; ++i) {
754 unsigned int offset = piece->argument[0] + (piece->argument[1] << 8);
755 new_piece = std::make_shared<CompressionPiece>(
756 piece->command, length_left, piece->argument, piece->argument_length);
758 new_piece->argument[0] =
763 new_piece->argument[1] =
769 return absl::InvalidArgumentError(
770 "SplitCompressionCommand: Invalid Command");
778 unsigned int pos = 0;
780 std::vector<uint8_t> output;
782 while (piece !=
nullptr) {
784 output.push_back(
BUILD_HEADER(piece->command, piece->length));
789 ((uint8_t)piece->command << 2) |
790 (((piece->length - 1) & 0xFF00) >> 8));
792 printf(
"Building extended header : cmd: %d, length: %d - %02X\n",
793 piece->command, piece->length, output[pos - 1]);
794 output.push_back(((piece->length - 1) & 0x00FF));
799 if (!new_piece.ok()) {
800 std::cout << new_piece.status().ToString() << std::endl;
802 printf(
"New added piece\n");
803 auto piece_data = new_piece.value();
805 piece_data->next = piece->next;
806 piece->next = piece_data;
813 tmp[0] = piece->argument[0];
814 tmp[1] = piece->argument[1];
816 tmp[0] = piece->argument[1];
817 tmp[1] = piece->argument[0];
819 for (
const auto& each : tmp) {
820 output.push_back(each);
824 for (
int i = 0; i < piece->argument_length; ++i) {
825 output.push_back(piece->argument[i]);
829 pos += piece->argument_length;
837 int mode,
int start,
int src_data_pos) {
838 if (chain_head->next !=
nullptr) {
845 if (!std::equal(decomp_data.begin() + start, decomp_data.end(),
847 return absl::InternalError(absl::StrFormat(
848 "Compressed data does not match uncompressed data at %d\n",
849 (
uint)(src_data_pos - start)));
852 return absl::OkStatus();
859 while (piece !=
nullptr) {
863 unsigned int previous_length = piece->length;
864 piece->length = piece->length + piece->next->length;
866 for (
int i = 0; i < piece->next->argument_length; ++i) {
867 piece->argument[i + previous_length] = piece->next->argument[i];
869 piece->argument_length = piece->length;
872 auto p_next_next = piece->next->next;
873 piece->next = p_next_next;
881absl::StatusOr<std::vector<uint8_t>>
CompressV2(
const uint8_t* data,
883 const int length,
int mode,
887 return std::vector<uint8_t>();
891 std::string worst_case =
"aaa";
892 auto compressed_chain =
893 std::make_shared<CompressionPiece>(1, 1, worst_case, 2);
894 auto compressed_chain_start = compressed_chain;
900 unsigned int src_pos = start;
901 unsigned int last_pos = start + length - 1;
902 unsigned int comp_accumulator = 0;
913 unsigned int max_win = 2;
927 if (comp_accumulator == 32 || src_pos > last_pos) {
928 std::string buffer =
SetBuffer(data, src_pos, comp_accumulator);
929 auto new_comp_piece = std::make_shared<CompressionPiece>(
931 compressed_chain->next = new_comp_piece;
932 comp_accumulator = 0;
936 src_pos, comp_accumulator, cmd_with_max,
940 if (src_pos > last_pos) {
941 printf(
"Breaking compression loop\n");
970 const std::vector<uint8_t> data,
const int pos,
const int length) {
975 unsigned int pos = context.
src_pos;
978 if (pos == 0 || context.
data[pos - 1] != context.
data[pos]) {
979 char byte_to_repeat = context.
data[pos];
980 while (pos <= context.
last_pos && context.
data[pos] == byte_to_repeat) {
988 DEBUG_LOG(
"CheckByteRepeatV3: byte_to_repeat = "
989 << (
int)byte_to_repeat <<
", size = "
996 unsigned int pos = context.
src_pos;
997 char byte1 = context.
data[pos];
998 char byte2 = context.
data[pos + 1];
1001 while (pos + 1 <= context.
last_pos) {
1002 if (context.
data[pos] == byte1 && context.
data[pos + 1] == byte2)
1021 unsigned int pos = context.
src_pos;
1022 uint8_t
byte = context.
data[pos];
1027 while (pos <= context.
last_pos &&
byte == context.
data[pos]) {
1036 context.
src_pos > 0 && pos < context.
data.size() &&
1053 const int window_size =
1059 context.
src_pos + window_size <= context.
data.size()) {
1060 unsigned int max_copied_size = 0;
1061 unsigned int best_search_start = 0;
1064 for (
int win_pos = 1; win_pos < window_size && win_pos < context.
src_pos;
1066 auto start_search_from = context.
data.begin() + context.
src_pos - win_pos;
1067 auto search_end = context.
data.begin() + context.
src_pos;
1071 auto found_pos = std::search(
1072 start_search_from, search_end, context.
data.begin() + context.
src_pos,
1073 context.
data.begin() + context.
src_pos + win_pos);
1075 if (found_pos != search_end) {
1077 unsigned int len = 0;
1078 while (context.
src_pos + len < context.
data.size() &&
1079 context.
data[context.
src_pos + len] == *(found_pos + len)) {
1083 if (len > max_copied_size) {
1084 max_copied_size = len;
1085 best_search_start = found_pos - context.
data.begin();
1090 if (max_copied_size >
1092 DEBUG_LOG(
"CheckIntraCopyV3: Detected repeating sequence of length "
1093 << max_copied_size <<
" starting from " << best_search_start);
1098 best_search_start >> 8;
1101 DEBUG_LOG(
"CheckIntraCopyV3: max_copied_size = " << max_copied_size
1102 <<
", best_search_start = "
1103 << best_search_start);
1124 DEBUG_LOG(
"CheckAvailableCompressionCommands: src_pos = " << context.
src_pos);
1128 int max_net_savings = -1;
1133 for (
unsigned int cmd_i = 1; cmd_i < 5; cmd_i++) {
1144 context.
src_pos + cmd_size_taken < context.
data.size()) {
1145 char prev_byte = context.
data[context.
src_pos - 1];
1146 char next_byte = context.
data[context.
src_pos + cmd_size_taken];
1147 if (prev_byte != next_byte && cmd_size_taken == 3) {
1153 if (net_savings > max_net_savings) {
1155 max_net_savings = net_savings;
1159 DEBUG_LOG(
"DetermineBestCompression: cmd_with_max = "
1171 std::vector<uint8_t> uncompressed_data(
1175 uncompressed_data.begin(),
1176 uncompressed_data.end());
1202 std::vector<uint8_t> uncompressed_data(
1206 uncompressed_data.begin(),
1207 uncompressed_data.end());
1212 <<
", compressed_data size = "
1217 DEBUG_LOG(
"AddCompressionToChain: Adding command arguments: ");
1224 std::vector<uint8_t> uncompressed_data(
1228 uncompressed_data.begin(),
1229 uncompressed_data.end());
1239 DEBUG_LOG(
"AddCompressionToChain: (Before) src_pos = "
1254 DEBUG_LOG(
"AddCompressionToChain: (After) src_pos = "
1267 if (!std::equal(decomp_data.begin() + context.
start, decomp_data.end(),
1268 temp_rom.
begin())) {
1269 return absl::InternalError(absl::StrFormat(
1270 "Compressed data does not match uncompressed data at %d\n",
1274 return absl::OkStatus();
1296 std::string empty_string =
"";
1300 for (
int i = 0; i < length_left; ++i) {
1320 return absl::InvalidArgumentError(
1321 "SplitCompressionCommand: Invalid Command");
1329 unsigned int pos = 0;
1340 (((piece.length - 1) & 0xFF00) >> 8));
1342 std::cout <<
"Building extended header : cmd: " << piece.command
1343 <<
", length: " << piece.length <<
" - "
1346 ((piece.length - 1) & 0x00FF));
1350 if (!new_piece.ok()) {
1351 std::cout << new_piece.status().ToString() << std::endl;
1361 tmp[0] = piece.argument[0];
1362 tmp[1] = piece.argument[1];
1364 tmp[0] = piece.argument[1];
1365 tmp[1] = piece.argument[0];
1367 for (
const auto& each : tmp) {
1372 for (
int i = 0; i < piece.argument_length; ++i) {
1377 pos += piece.argument_length;
1391 DEBUG_LOG(
"FinalizeCompression: compressed_data size = "
1396 const std::vector<uint8_t>& data,
const int start,
const int length,
1397 int mode,
bool check) {
1399 return std::vector<uint8_t>();
1427std::string
SetBuffer(
const uint8_t* data,
int src_pos,
int comp_accumulator) {
1429 for (
int i = 0; i < comp_accumulator; ++i) {
1430 buffer.push_back(data[i + src_pos - comp_accumulator]);
1435std::string
SetBuffer(
const std::vector<uint8_t>& data,
int src_pos,
1436 int comp_accumulator) {
1438 for (
int i = 0; i < comp_accumulator; ++i) {
1439 buffer.push_back(data[i + src_pos - comp_accumulator]);
1444void memfill(
const uint8_t* data, std::vector<uint8_t>& buffer,
int buffer_pos,
1445 int offset,
int length) {
1446 auto a = data[offset];
1447 auto b = data[offset + 1];
1448 for (
int i = 0; i < length; i = i + 2) {
1449 buffer[buffer_pos + i] = a;
1450 if ((i + 1) < length)
1451 buffer[buffer_pos + i + 1] = b;
1456 int offset,
int size,
1457 int mode,
size_t rom_size) {
1459 return std::vector<uint8_t>();
1464 if (rom_size !=
static_cast<size_t>(-1) &&
1465 (offset < 0 ||
static_cast<size_t>(offset) >= rom_size)) {
1466 return absl::OutOfRangeError(
1467 absl::StrFormat(
"DecompressV2: Initial offset %d exceeds ROM size %zu",
1471 std::vector<uint8_t> buffer(size, 0);
1472 unsigned int length = 0;
1473 unsigned int buffer_pos = 0;
1474 uint8_t command = 0;
1477 if (rom_size !=
static_cast<size_t>(-1) &&
1478 static_cast<size_t>(offset) >= rom_size) {
1479 return absl::OutOfRangeError(
1480 absl::StrFormat(
"DecompressV2: Initial offset %d exceeds ROM size %zu",
1483 uint8_t header = data[offset];
1487 if (rom_size !=
static_cast<size_t>(-1) &&
1488 static_cast<size_t>(offset) >= rom_size) {
1489 return absl::OutOfRangeError(absl::StrFormat(
1490 "DecompressV2: Offset %d exceeds ROM size %zu while reading command",
1496 if (rom_size !=
static_cast<size_t>(-1) &&
1497 static_cast<size_t>(offset + 1) >= rom_size) {
1498 return absl::OutOfRangeError(
1499 absl::StrFormat(
"DecompressV2: Offset %d+1 exceeds ROM size %zu "
1500 "for expanded command",
1517 if (buffer_pos + length >
static_cast<unsigned int>(size)) {
1519 int new_size = size;
1520 while (buffer_pos + length >
static_cast<unsigned int>(new_size)) {
1521 if (new_size > INT_MAX / 2) {
1522 return absl::ResourceExhaustedError(absl::StrFormat(
1523 "DecompressV2: Buffer size overflow at pos %u, length %u",
1524 buffer_pos, length));
1529 buffer.resize(size);
1535 if (rom_size !=
static_cast<size_t>(-1) &&
1536 static_cast<size_t>(offset + length) > rom_size) {
1537 return absl::OutOfRangeError(
1538 absl::StrFormat(
"DecompressV2: DirectCopy offset %d + length %u "
1539 "exceeds ROM size %zu",
1540 offset, length, rom_size));
1542 memcpy(buffer.data() + buffer_pos, data + offset, length);
1543 buffer_pos += length;
1548 if (rom_size !=
static_cast<size_t>(-1) &&
1549 static_cast<size_t>(offset) >= rom_size) {
1550 return absl::OutOfRangeError(absl::StrFormat(
1551 "DecompressV2: ByteFill offset %d exceeds ROM size %zu", offset,
1554 memset(buffer.data() + buffer_pos, (
int)(data[offset]), length);
1555 buffer_pos += length;
1560 if (rom_size !=
static_cast<size_t>(-1) &&
1561 static_cast<size_t>(offset + 1) >= rom_size) {
1562 return absl::OutOfRangeError(absl::StrFormat(
1563 "DecompressV2: WordFill offset %d+1 exceeds ROM size %zu", offset,
1566 memfill(data, buffer, buffer_pos, offset, length);
1567 buffer_pos += length;
1572 if (rom_size !=
static_cast<size_t>(-1) &&
1573 static_cast<size_t>(offset) >= rom_size) {
1574 return absl::OutOfRangeError(absl::StrFormat(
1575 "DecompressV2: IncreasingFill offset %d exceeds ROM size %zu",
1578 auto inc_byte = data[offset];
1579 for (
unsigned int i = 0; i < length; i++) {
1580 buffer[buffer_pos] = inc_byte++;
1587 if (rom_size !=
static_cast<size_t>(-1) &&
1588 static_cast<size_t>(offset + 1) >= rom_size) {
1589 return absl::OutOfRangeError(absl::StrFormat(
1590 "DecompressV2: RepeatingBytes offset %d+1 exceeds ROM size %zu",
1593 uint16_t s1 = ((data[offset + 1] &
kSnesByteMax) << 8);
1595 int addr = (s1 | s2);
1603 if (
static_cast<unsigned int>(addr) >= buffer_pos) {
1604 return absl::InternalError(absl::StrFormat(
1605 "Decompress: copy source %#06x is not before the output "
1606 "position %#06x (stream offset %#06x)",
1607 addr, buffer_pos, offset));
1609 if (
static_cast<size_t>(addr) + length > buffer.size()) {
1610 return absl::OutOfRangeError(absl::StrFormat(
1611 "DecompressV2: copy source %#06x+%d is outside the output", addr,
1618 for (
int i = 0; i < length; ++i) {
1619 buffer[buffer_pos + i] = buffer[addr + i];
1621 buffer_pos += length;
1625 std::cout << absl::StrFormat(
1626 "Decompress: Invalid header (Offset : %#06x, Command: %#04x)\n",
1631 if (rom_size !=
static_cast<size_t>(-1) &&
1632 static_cast<size_t>(offset) >= rom_size) {
1633 return absl::OutOfRangeError(
1634 absl::StrFormat(
"DecompressV2: Offset %d exceeds ROM size %zu while "
1635 "reading next header",
1638 header = data[offset];
1648 bool big_endian_copy) {
1649 if (data ==
nullptr || offset >= data_size) {
1650 return absl::OutOfRangeError(
1651 absl::StrFormat(
"LC-LZ2 stream offset 0x%06zX is outside %zu bytes",
1652 offset, data_size));
1656 size_t pos = offset;
1657 auto next_byte = [&]() -> absl::StatusOr<uint8_t> {
1658 if (pos >= data_size) {
1659 return absl::OutOfRangeError(absl::StrFormat(
1660 "LC-LZ2 stream at 0x%06zX is truncated at 0x%06zX", offset, pos));
1676 length = ((
static_cast<size_t>(header & 0x03) << 8) | low) + 1;
1678 command = header >> 5;
1682 if (result.
data.size() + length > max_output) {
1683 return absl::OutOfRangeError(
1684 absl::StrFormat(
"LC-LZ2 stream at 0x%06zX decodes past %zu bytes",
1685 offset, max_output));
1690 for (
size_t i = 0; i < length; ++i) {
1692 result.
data.push_back(value);
1697 result.
data.insert(result.
data.end(), length, value);
1703 for (
size_t i = 0; i < length; ++i) {
1704 result.
data.push_back((i & 1) == 0 ? first : second);
1710 for (
size_t i = 0; i < length; ++i) {
1711 result.
data.push_back(value++);
1720 big_endian_copy ? (first << 8) | second : (second << 8) | first;
1721 for (
size_t i = 0; i < length; ++i) {
1722 if (source >= result.
data.size()) {
1723 return absl::InvalidArgumentError(absl::StrFormat(
1724 "LC-LZ2 stream at 0x%06zX copies from output offset 0x%04zX "
1725 "before it is written",
1728 result.
data.push_back(result.
data[source++]);
1740 int pos,
int size) {
1745 int pos,
int size) {
1750 const std::vector<uint8_t> data,
int pos,
int size) {
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status LoadFromData(const std::vector< uint8_t > &data, const LoadOptions &options=LoadOptions::Defaults())
#define BUILD_HEADER(command, length)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
void CheckIntraCopyV3(CompressionContext &context)
absl::StatusOr< std::vector< uint8_t > > CompressGraphics(const uint8_t *data, const int pos, const int length)
absl::StatusOr< std::vector< uint8_t > > CompressV3(const std::vector< uint8_t > &data, const int start, const int length, int mode, bool check)
Compresses a buffer of data using the LC_LZ2 algorithm.
constexpr int kExpandedLengthMod
constexpr int kCommandDirectCopy
void memfill(const uint8_t *data, std::vector< uint8_t > &buffer, int buffer_pos, int offset, int length)
void FinalizeCompression(CompressionContext &context)
void CheckWordRepeatV2(const uint8_t *data, uint &src_pos, const unsigned int last_pos, CompressionCommand &cmd)
void InitializeCompression(CompressionContext &context)
absl::StatusOr< std::vector< uint8_t > > DecompressV2(const uint8_t *data, int offset, int size, int mode, size_t rom_size)
Decompresses a buffer of data using the LC_LZ2 algorithm.
struct CompressionPiece CompressionPiece
absl::StatusOr< std::vector< uint8_t > > CompressOverworld(const uint8_t *data, const int pos, const int length)
constexpr int kSnesByteMax
constexpr int kNintendoMode1
void AddAlternativeCompressionCommand(const uint8_t *rom_data, CompressionPiecePointer &compressed_chain, const CompressionCommand &command, uint &source_data_position, uint &uncompressed_data_size, uint &best_command, uint &best_command_gain)
std::shared_ptr< CompressionPiece > CompressionPiecePointer
void CheckIncByteV3(CompressionContext &context)
absl::Status ValidateCompressionResultV3(const CompressionContext &context)
constexpr int kNintendoMode2
std::string SetBuffer(const uint8_t *data, int src_pos, int comp_accumulator)
constexpr int kCommandRepeatingBytes
std::array< std::array< char, 2 >, 5 > CommandArgumentArray
void CheckByteRepeat(const uint8_t *rom_data, DataSizeArray &data_size_taken, CommandArgumentArray &cmd_args, uint &src_data_pos, const unsigned int last_pos)
void CheckByteRepeatV2(const uint8_t *data, uint &src_pos, const unsigned int last_pos, CompressionCommand &cmd)
void HandleDirectCopy(CompressionContext &context)
void CheckIncByteV2(const uint8_t *rom_data, uint &src_data_pos, const unsigned int last_pos, CompressionCommand &cmd)
absl::StatusOr< std::vector< uint8_t > > DecompressGraphics(const uint8_t *data, int pos, int size)
constexpr int kCommandMod
void PrintCompressionPiece(const CompressionPiecePointer &piece)
constexpr int kCommandWordFill
constexpr int kMaxLengthNormalHeader
void CheckAvailableCompressionCommands(CompressionContext &context)
constexpr int kMaxLengthCompression
void CompressionCommandAlternativeV2(const uint8_t *rom_data, const CompressionCommand &cmd, CompressionPiecePointer &compressed_chain, uint &src_data_pos, uint &comp_accumulator, uint &cmd_with_max, uint &max_win)
void CheckIntraCopyV2(const uint8_t *rom_data, uint &src_data_pos, const unsigned int last_pos, unsigned int start, CompressionCommand &cmd)
std::vector< uint8_t > CreateCompressionString(CompressionPiecePointer &start, int mode)
absl::StatusOr< std::vector< uint8_t > > DecompressOverworld(const uint8_t *data, int pos, int size)
std::array< uint, 5 > DataSizeArray
constexpr int kCommandIncreasingFill
void ValidateForByteGainV2(const CompressionCommand &cmd, uint &max_win, uint &cmd_with_max)
void ValidateForByteGain(const DataSizeArray &data_size_taken, const CommandSizeArray &cmd_size, uint &max_win, uint &cmd_with_max)
constexpr int kCommandByteFill
void PrintCompressionChain(const CompressionPiecePointer &chain_head)
void CheckIncByte(const uint8_t *rom_data, DataSizeArray &data_size_taken, CommandArgumentArray &cmd_args, uint &src_data_pos, const unsigned int last_pos)
constexpr int kExpandedMod
const std::array< int, 5 > kCommandSizes
void AddCompressionToChain(CompressionContext &context)
absl::StatusOr< CompressionPiecePointer > SplitCompressionPiece(CompressionPiecePointer &piece, int mode)
void CompressionCommandAlternative(const uint8_t *rom_data, CompressionPiecePointer &compressed_chain, const CommandSizeArray &cmd_size, const CommandArgumentArray &cmd_args, uint &src_data_pos, uint &comp_accumulator, uint &cmd_with_max, uint &max_win)
constexpr int kNormalLengthMod
constexpr int kCompressionStringMod
absl::StatusOr< ExactDecodeResult > DecompressExact(const uint8_t *data, size_t data_size, size_t offset, size_t max_output, bool big_endian_copy)
Decodes one LC-LZ2 stream with the semantics of the ALTTP bank-00 decompressor (Decompress at $00:E79...
void CheckWordRepeat(const uint8_t *rom_data, DataSizeArray &data_size_taken, CommandArgumentArray &cmd_args, uint &src_data_pos, const unsigned int last_pos)
void CheckByteRepeatV3(CompressionContext &context)
CompressionPiecePointer MergeCopy(CompressionPiecePointer &start)
std::array< uint, 5 > CommandSizeArray
void DetermineBestCompression(CompressionContext &context)
absl::StatusOr< CompressionPiece > SplitCompressionPieceV3(CompressionPiece &piece, int mode)
absl::StatusOr< std::vector< uint8_t > > CompressV2(const uint8_t *data, const int start, const int length, int mode, bool check)
Compresses a buffer of data using the LC_LZ2 algorithm.
void CheckWordRepeatV3(CompressionContext &context)
void CheckIntraCopy(const uint8_t *rom_data, DataSizeArray &data_size_taken, CommandArgumentArray &cmd_args, uint &src_data_pos, const unsigned int last_pos, unsigned int start)
absl::Status ValidateCompressionResult(CompressionPiecePointer &chain_head, int mode, int start, int src_data_pos)
std::vector< uint8_t > HyruleMagicDecompress(uint8_t const *src, int *const size, int const p_big_endian, size_t max_src_size)
std::vector< uint8_t > HyruleMagicCompress(uint8_t const *const src, int const oldsize, int *const size, int const flag)
uint16_t ldle16b(uint8_t const *const p_arr)
void stle16b(uint8_t *const p_arr, uint16_t const p_val)
#define RETURN_IF_ERROR(expr)
std::array< uint, 5 > data_size
std::array< uint, 5 > cmd_size
std::array< std::array< char, 2 >, 5 > arguments
std::vector< uint8_t > data
CompressionCommand current_cmd
std::vector< CompressionPiece > compression_pieces
std::vector< uint8_t > compression_string
unsigned int cmd_with_max
std::vector< uint8_t > compressed_data
unsigned int comp_accumulator
One LC-LZ2 stream decoded the way the game decodes it.
std::vector< uint8_t > data