yaze 0.2.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
compression.cc
Go to the documentation of this file.
1#include "compression.h"
2
3#include <iostream>
4#include <memory>
5#include <string>
6
7#include "absl/status/status.h"
8#include "absl/status/statusor.h"
9#include "app/rom.h"
11#include "util/macro.h"
12
13#define DEBUG_LOG(msg) std::cout << msg << std::endl
14
15namespace yaze {
16namespace gfx {
17
18std::vector<uint8_t> HyruleMagicCompress(uint8_t const* const src,
19 int const oldsize, int* const size,
20 int const flag) {
21 unsigned char* b2 =
22 (unsigned char*)malloc(0x1000); // allocate a 2^12 sized buffer
23
24 int i, j, k, l, m = 0, n, o = 0, bd = 0, p, q = 0, r;
25
26 for (i = 0; i < oldsize;) {
27 l = src[i]; // grab a char from the buffer.
28
29 k = 0;
30
31 r = !!q; // r = the same logical value (0 or 1) as q, but not the same
32 // value necesarily.
33
34 for (j = 0; j < i - 1; j++) {
35 if (src[j] == l) {
36 m = oldsize - j;
37
38 for (n = 0; n < m; n++)
39 if (src[n + j] != src[n + i]) break;
40
41 if (n > k) k = n, o = j;
42 }
43 }
44
45 for (n = i + 1; n < oldsize; n++) {
46 if (src[n] != l) {
47 // look for chars identical to the first one.
48 // stop if we can't find one.
49 // n will reach i+k+1 for some k >= 0.
50
51 break;
52 }
53 }
54
55 n -= i; // offset back by i. i.e. n = k+1 as above.
56
57 if (n > 1 + r)
58 p = 1;
59 else {
60 m = src[i + 1];
61
62 for (n = i + 2; n < oldsize; n++) {
63 if (src[n] != l) break;
64
65 n++;
66
67 if (src[n] != m) break;
68 }
69
70 n -= i;
71
72 if (n > 2 + r)
73 p = 2;
74 else {
75 m = oldsize - i;
76
77 for (n = 1; n < m; n++)
78 if (src[i + n] != l + n) break;
79
80 if (n > 1 + r)
81 p = 3;
82 else
83 p = 0;
84 }
85 }
86
87 if (k > 3 + r && k > n + (p & 1)) p = 4, n = k;
88
89 if (!p)
90 q++, i++;
91 else {
92 if (q) {
93 q--;
94
95 if (q > 31) {
96 b2[bd++] = (unsigned char)(224 + (q >> 8));
97 }
98
99 b2[bd++] = (unsigned char)q;
100 q++;
101
102 memcpy(b2 + bd, src + i - q, q);
103
104 bd += q;
105 q = 0;
106 }
107
108 i += n;
109 n--;
110
111 if (n > 31) {
112 b2[bd++] = (unsigned char)(224 + (n >> 8) + (p << 2));
113 b2[bd++] = (unsigned char)n;
114 } else
115 b2[bd++] = (unsigned char)((p << 5) + n);
116
117 switch (p) {
118 case 1:
119 case 3:
120 b2[bd++] = (unsigned char)l;
121 break;
122
123 case 2:
124 b2[bd++] = (unsigned char)l;
125 b2[bd++] = (unsigned char)m;
126
127 break;
128
129 case 4:
130 if (flag) {
131 b2[bd++] = (unsigned char)(o >> 8);
132 b2[bd++] = (unsigned char)o;
133 } else {
134 b2[bd++] = (unsigned char)o;
135 b2[bd++] = (unsigned char)(o >> 8);
136 }
137 }
138
139 continue;
140 }
141 }
142
143 if (q) {
144 q--;
145
146 if (q > 31) {
147 b2[bd++] = (unsigned char)(224 + (q >> 8));
148 }
149
150 b2[bd++] = (unsigned char)q;
151 q++;
152
153 memcpy(b2 + bd, src + i - q, q);
154
155 bd += q;
156 }
157
158 b2[bd++] = 255;
159 b2 = (unsigned char*)realloc(b2, bd);
160 *size = bd;
161
162 std::vector<uint8_t> compressed_data(b2, b2 + bd);
163 free(b2);
164 return compressed_data;
165}
166
167std::vector<uint8_t> HyruleMagicDecompress(uint8_t const* src, int* const size,
168 int const p_big_endian) {
169 unsigned char* b2 = (unsigned char*)malloc(1024);
170
171 int bd = 0, bs = 1024;
172
173 unsigned char a;
174 unsigned char b;
175 unsigned short c, d;
176
177 for (;;) {
178 // retrieve a uint8_t from the buffer.
179 a = *(src++);
180
181 // end the decompression routine if we encounter 0xff.
182 if (a == 0xff) break;
183
184 // examine the top 3 bits of a.
185 b = (a >> 5);
186
187 if (b == 7) // i.e. 0b 111
188 {
189 // get bits 0b 0001 1100
190 b = ((a >> 2) & 7);
191
192 // get bits 0b 0000 0011, multiply by 256, OR with the next byte.
193 c = ((a & 0x0003) << 8);
194 c |= *(src++);
195 } else
196 // or get bits 0b 0001 1111
197 c = (uint16_t)(a & 31);
198
199 c++;
200
201 if ((bd + c) > (bs - 512)) {
202 // need to increase the buffer size.
203 bs += 1024;
204 b2 = (uint8_t*)realloc(b2, bs);
205 }
206
207 // 7 was handled, here we handle other decompression codes.
208
209 switch (b) {
210 case 0: // 0b 000
211
212 // raw copy
213
214 // copy info from the src buffer to our new buffer,
215 // at offset bd (which we'll be increasing;
216 memcpy(b2 + bd, src, c);
217
218 // increment the src pointer accordingly.
219 src += c;
220
221 bd += c;
222
223 break;
224
225 case 1: // 0b 001
226
227 // rle copy
228
229 // make c duplicates of one byte, inc the src pointer.
230 memset(b2 + bd, *(src++), c);
231
232 // increase the b2 offset.
233 bd += c;
234
235 break;
236
237 case 2: // 0b 010
238
239 // rle 16-bit alternating copy
240
241 d = zelda3::ldle16b(src);
242
243 src += 2;
244
245 while (c > 1) {
246 // copy that 16-bit number c/2 times into the b2 buffer.
247 zelda3::stle16b(b2 + bd, d);
248
249 bd += 2;
250 c -= 2; // hence c/2
251 }
252
253 if (c) // if there's a remainder of c/2, this handles it.
254 b2[bd++] = (char)d;
255
256 break;
257
258 case 3: // 0b 011
259
260 // incrementing copy
261
262 // get the current src byte.
263 a = *(src++);
264
265 while (c--) {
266 // increment that byte and copy to b2 in c iterations.
267 // e.g. a = 4, b2 will have 4,5,6,7,8... written to it.
268 b2[bd++] = a++;
269 }
270
271 break;
272
273 default: // 0b 100, 101, 110
274
275 // lz copy
276
277 if (p_big_endian) {
278 d = (*src << 8) + src[1];
279 } else {
280 d = zelda3::ldle16b(src);
281 }
282
283 while (c--) {
284 // copy from a different location in the buffer.
285 b2[bd++] = b2[d++];
286 }
287
288 src += 2;
289 }
290 }
291
292 b2 = (unsigned char*)realloc(b2, bd);
293
294 if (size) (*size) = bd;
295
296 // return the unsigned char* buffer b2, which contains the uncompressed data.
297 std::vector<uint8_t> decompressed_data(b2, b2 + bd);
298 free(b2);
299 return decompressed_data;
300}
301
302namespace lc_lz2 {
303
305 std::cout << "Command: " << std::to_string(piece->command) << "\n";
306 std::cout << "Command Length: " << piece->length << "\n";
307 std::cout << "Argument: ";
308 auto arg_size = piece->argument.size();
309 for (int i = 0; i < arg_size; ++i) {
310 printf("%02X ", piece->argument.at(i));
311 }
312 std::cout << "\nArgument Length: " << piece->argument_length << "\n";
313}
314
316 auto compressed_chain = chain_head->next;
317 while (compressed_chain != nullptr) {
318 std::cout << "- Compression Piece -\n";
319 PrintCompressionPiece(compressed_chain);
320 compressed_chain = compressed_chain->next;
321 }
322}
323
324void CheckByteRepeat(const uint8_t* rom_data, DataSizeArray& data_size_taken,
325 CommandArgumentArray& cmd_args, uint& src_data_pos,
326 const unsigned int last_pos) {
327 unsigned int pos = src_data_pos;
328 char byte_to_repeat = rom_data[pos];
329 while (pos <= last_pos && rom_data[pos] == byte_to_repeat) {
330 data_size_taken[kCommandByteFill]++;
331 pos++;
332 }
333 cmd_args[kCommandByteFill][0] = byte_to_repeat;
334}
335
336void CheckWordRepeat(const uint8_t* rom_data, DataSizeArray& data_size_taken,
337 CommandArgumentArray& cmd_args, uint& src_data_pos,
338 const unsigned int last_pos) {
339 if (src_data_pos + 2 <= last_pos &&
340 rom_data[src_data_pos] != rom_data[src_data_pos + 1]) {
341 unsigned int pos = src_data_pos;
342 char byte1 = rom_data[pos];
343 char byte2 = rom_data[pos + 1];
344 pos += 2;
345 data_size_taken[kCommandWordFill] = 2;
346 while (pos + 1 <= last_pos) {
347 if (rom_data[pos] == byte1 && rom_data[pos + 1] == byte2)
348 data_size_taken[kCommandWordFill] += 2;
349 else
350 break;
351 pos += 2;
352 }
353 cmd_args[kCommandWordFill][0] = byte1;
354 cmd_args[kCommandWordFill][1] = byte2;
355 }
356}
357
358void CheckIncByte(const uint8_t* rom_data, DataSizeArray& data_size_taken,
359 CommandArgumentArray& cmd_args, uint& src_data_pos,
360 const unsigned int last_pos) {
361 unsigned int pos = src_data_pos;
362 char byte = rom_data[pos];
363 pos++;
364 data_size_taken[kCommandIncreasingFill] = 1;
365 byte++;
366 while (pos <= last_pos && byte == rom_data[pos]) {
367 data_size_taken[kCommandIncreasingFill]++;
368 byte++;
369 pos++;
370 }
371 cmd_args[kCommandIncreasingFill][0] = rom_data[src_data_pos];
372}
373
374void CheckIntraCopy(const uint8_t* rom_data, DataSizeArray& data_size_taken,
375 CommandArgumentArray& cmd_args, uint& src_data_pos,
376 const unsigned int last_pos, unsigned int start) {
377 if (src_data_pos != start) {
378 unsigned int searching_pos = start;
379 unsigned int current_pos_u = src_data_pos;
380 unsigned int copied_size = 0;
381 unsigned int search_start = start;
382
383 while (searching_pos < src_data_pos && current_pos_u <= last_pos) {
384 while (rom_data[current_pos_u] != rom_data[searching_pos] &&
385 searching_pos < src_data_pos)
386 searching_pos++;
387 search_start = searching_pos;
388 while (current_pos_u <= last_pos &&
389 rom_data[current_pos_u] == rom_data[searching_pos] &&
390 searching_pos < src_data_pos) {
391 copied_size++;
392 current_pos_u++;
393 searching_pos++;
394 }
395 if (copied_size > data_size_taken[kCommandRepeatingBytes]) {
396 search_start -= start;
397 printf("- Found repeat of %d at %d\n", copied_size, search_start);
398 data_size_taken[kCommandRepeatingBytes] = copied_size;
399 cmd_args[kCommandRepeatingBytes][0] = search_start & kSnesByteMax;
400 cmd_args[kCommandRepeatingBytes][1] = search_start >> 8;
401 }
402 current_pos_u = src_data_pos;
403 copied_size = 0;
404 }
405 }
406}
407
408// Check if a command managed to pick up `max_win` or more bytes
409// Avoids being even with copy command, since it's possible to merge copy
410void ValidateForByteGain(const DataSizeArray& data_size_taken,
411 const CommandSizeArray& cmd_size, uint& max_win,
412 uint& cmd_with_max) {
413 for (unsigned int cmd_i = 1; cmd_i < 5; cmd_i++) {
414 unsigned int cmd_size_taken = data_size_taken[cmd_i];
415 // TODO(@scawful): Replace conditional with table of command sizes
416 // "Table that is even with copy but all other cmd are 2"
417 auto table_check =
418 !(cmd_i == kCommandRepeatingBytes && cmd_size_taken == 3);
419 if (cmd_size_taken > max_win && cmd_size_taken > cmd_size[cmd_i] &&
420 table_check) {
421 printf("==> C:%d / S:%d\n", cmd_i, cmd_size_taken);
422 cmd_with_max = cmd_i;
423 max_win = cmd_size_taken;
424 }
425 }
426}
427
428void CompressionCommandAlternative(const uint8_t* rom_data,
429 CompressionPiecePointer& compressed_chain,
430 const CommandSizeArray& cmd_size,
431 const CommandArgumentArray& cmd_args,
432 uint& src_data_pos, uint& comp_accumulator,
433 uint& cmd_with_max, uint& max_win) {
434 printf("- Ok we get a gain from %d\n", cmd_with_max);
435 std::string buffer;
436 buffer.push_back(cmd_args[cmd_with_max][0]);
437 if (cmd_size[cmd_with_max] == 2) {
438 buffer.push_back(cmd_args[cmd_with_max][1]);
439 }
440
441 auto new_comp_piece = std::make_shared<CompressionPiece>(
442 cmd_with_max, max_win, buffer, cmd_size[cmd_with_max]);
443 PrintCompressionPiece(new_comp_piece);
444 // If we let non compressed stuff, we need to add a copy chunk before
445 if (comp_accumulator != 0) {
446 std::string copy_buff;
447 copy_buff.resize(comp_accumulator);
448 for (int i = 0; i < comp_accumulator; ++i) {
449 copy_buff[i] = rom_data[i + src_data_pos - comp_accumulator];
450 }
451 auto copy_chunk = std::make_shared<CompressionPiece>(
452 kCommandDirectCopy, comp_accumulator, copy_buff, comp_accumulator);
453 compressed_chain->next = copy_chunk;
454 compressed_chain = copy_chunk;
455 } else {
456 compressed_chain->next = new_comp_piece;
457 compressed_chain = new_comp_piece;
458 }
459 src_data_pos += max_win;
460 comp_accumulator = 0;
461}
462
463void CheckByteRepeatV2(const uint8_t* data, uint& src_pos,
464 const unsigned int last_pos, CompressionCommand& cmd) {
465 unsigned int i = 0;
466 while (src_pos + i < last_pos && data[src_pos] == data[src_pos + i]) {
467 ++i;
468 }
470 cmd.arguments[kCommandByteFill][0] = data[src_pos];
471}
472
473void CheckWordRepeatV2(const uint8_t* data, uint& src_pos,
474 const unsigned int last_pos, CompressionCommand& cmd) {
475 if (src_pos + 2 <= last_pos && data[src_pos] != data[src_pos + 1]) {
476 unsigned int pos = src_pos;
477 char byte1 = data[pos];
478 char byte2 = data[pos + 1];
479 pos += 2;
481 while (pos + 1 <= last_pos) {
482 if (data[pos] == byte1 && data[pos + 1] == byte2)
483 cmd.data_size[kCommandWordFill] += 2;
484 else
485 break;
486 pos += 2;
487 }
488 cmd.arguments[kCommandWordFill][0] = byte1;
489 cmd.arguments[kCommandWordFill][1] = byte2;
490 }
491}
492
493void CheckIncByteV2(const uint8_t* rom_data, uint& src_data_pos,
494 const unsigned int last_pos, CompressionCommand& cmd) {
495 unsigned int pos = src_data_pos;
496 char byte = rom_data[pos];
497 pos++;
499 byte++;
500 while (pos <= last_pos && byte == rom_data[pos]) {
502 byte++;
503 pos++;
504 }
505 cmd.arguments[kCommandIncreasingFill][0] = rom_data[src_data_pos];
506}
507
508void CheckIntraCopyV2(const uint8_t* rom_data, uint& src_data_pos,
509 const unsigned int last_pos, unsigned int start,
510 CompressionCommand& cmd) {
511 if (src_data_pos != start) {
512 unsigned int searching_pos = start;
513 unsigned int current_pos_u = src_data_pos;
514 unsigned int copied_size = 0;
515 unsigned int search_start = start;
516
517 while (searching_pos < src_data_pos && current_pos_u <= last_pos) {
518 while (rom_data[current_pos_u] != rom_data[searching_pos] &&
519 searching_pos < src_data_pos)
520 searching_pos++;
521 search_start = searching_pos;
522 while (current_pos_u <= last_pos &&
523 rom_data[current_pos_u] == rom_data[searching_pos] &&
524 searching_pos < src_data_pos) {
525 copied_size++;
526 current_pos_u++;
527 searching_pos++;
528 }
529 if (copied_size > cmd.data_size[kCommandRepeatingBytes]) {
530 search_start -= start;
531 printf("- Found repeat of %d at %d\n", copied_size, search_start);
532 cmd.data_size[kCommandRepeatingBytes] = copied_size;
533 cmd.arguments[kCommandRepeatingBytes][0] = search_start & kSnesByteMax;
534 cmd.arguments[kCommandRepeatingBytes][1] = search_start >> 8;
535 }
536 current_pos_u = src_data_pos;
537 copied_size = 0;
538 }
539 }
540}
541
542// Table indicating command sizes, in bytes
543const std::array<int, 5> kCommandSizes = {1, 2, 2, 2, 3};
544
545// TODO(@scawful): TEST ME
546void ValidateForByteGainV2(const CompressionCommand& cmd, uint& max_win,
547 uint& cmd_with_max) {
548 for (unsigned int cmd_i = 1; cmd_i < 5; cmd_i++) {
549 unsigned int cmd_size_taken = cmd.data_size[cmd_i];
550 // Check if the command size exceeds the maximum win and the size in the
551 // command sizes table, except for the repeating bytes command when the size
552 // taken is 3
553 if (cmd_size_taken > max_win && cmd_size_taken > kCommandSizes[cmd_i] &&
554 !(cmd_i == kCommandRepeatingBytes && cmd_size_taken == 3)) {
555 printf("==> C:%d / S:%d\n", cmd_i, cmd_size_taken);
556 cmd_with_max = cmd_i;
557 max_win = cmd_size_taken;
558 }
559 }
560}
561
562void CompressionCommandAlternativeV2(const uint8_t* rom_data,
563 const CompressionCommand& cmd,
564 CompressionPiecePointer& compressed_chain,
565 uint& src_data_pos, uint& comp_accumulator,
566 uint& cmd_with_max, uint& max_win) {
567 printf("- Ok we get a gain from %d\n", cmd_with_max);
568 std::string buffer;
569 buffer.push_back(cmd.arguments[cmd_with_max][0]);
570 if (cmd.cmd_size[cmd_with_max] == 2) {
571 buffer.push_back(cmd.arguments[cmd_with_max][1]);
572 }
573
574 auto new_comp_piece = std::make_shared<CompressionPiece>(
575 cmd_with_max, max_win, buffer, cmd.cmd_size[cmd_with_max]);
576 PrintCompressionPiece(new_comp_piece);
577 // If we let non compressed stuff, we need to add a copy chunk before
578 if (comp_accumulator != 0) {
579 std::string copy_buff;
580 copy_buff.resize(comp_accumulator);
581 for (int i = 0; i < comp_accumulator; ++i) {
582 copy_buff[i] = rom_data[i + src_data_pos - comp_accumulator];
583 }
584 auto copy_chunk = std::make_shared<CompressionPiece>(
585 kCommandDirectCopy, comp_accumulator, copy_buff, comp_accumulator);
586 compressed_chain->next = copy_chunk;
587 compressed_chain = copy_chunk;
588 } else {
589 compressed_chain->next = new_comp_piece;
590 compressed_chain = new_comp_piece;
591 }
592 src_data_pos += max_win;
593 comp_accumulator = 0;
594}
595
597 const uint8_t* rom_data, CompressionPiecePointer& compressed_chain,
598 const CompressionCommand& command, uint& source_data_position,
599 uint& uncompressed_data_size, uint& best_command, uint& best_command_gain) {
600 std::cout << "- Identified a gain from command: " << best_command
601 << std::endl;
602
603 // Create a buffer to store the arguments for the best command.
604 std::string argument_buffer;
605 argument_buffer.push_back(command.arguments[best_command][0]);
606 if (command.cmd_size[best_command] == 2) {
607 argument_buffer.push_back(command.arguments[best_command][1]);
608 }
609
610 // Create a new compression piece for the best command.
611 auto new_compression_piece = std::make_shared<CompressionPiece>(
612 best_command, best_command_gain, argument_buffer,
613 command.cmd_size[best_command]);
614 PrintCompressionPiece(new_compression_piece);
615
616 // If there is uncompressed data, create a direct copy compression piece for
617 // it.
618 if (uncompressed_data_size != 0) {
619 std::string copy_buffer(uncompressed_data_size, 0);
620 for (int i = 0; i < uncompressed_data_size; ++i) {
621 copy_buffer[i] =
622 rom_data[i + source_data_position - uncompressed_data_size];
623 }
624 auto direct_copy_piece = std::make_shared<CompressionPiece>(
625 kCommandDirectCopy, uncompressed_data_size, copy_buffer,
626 uncompressed_data_size);
627
628 // Append the direct copy piece to the chain.
629 compressed_chain->next = direct_copy_piece;
630 compressed_chain = direct_copy_piece;
631 }
632
633 // Append the new compression piece to the chain.
634 compressed_chain->next = new_compression_piece;
635 compressed_chain = new_compression_piece;
636
637 // Update the position in the source data and reset the uncompressed data
638 // size.
639 source_data_position += best_command_gain;
640 uncompressed_data_size = 0;
641}
642
643absl::StatusOr<CompressionPiecePointer> SplitCompressionPiece(
644 CompressionPiecePointer& piece, int mode) {
645 CompressionPiecePointer new_piece;
646 unsigned int length_left = piece->length - kMaxLengthCompression;
647 piece->length = kMaxLengthCompression;
648
649 switch (piece->command) {
650 case kCommandByteFill:
651 case kCommandWordFill:
652 new_piece = std::make_shared<CompressionPiece>(
653 piece->command, length_left, piece->argument, piece->argument_length);
654 break;
656 new_piece = std::make_shared<CompressionPiece>(
657 piece->command, length_left, piece->argument, piece->argument_length);
658 new_piece->argument[0] =
659 (char)(piece->argument[0] + kMaxLengthCompression);
660 break;
661 case kCommandDirectCopy: {
662 std::string empty;
663 piece->argument_length = kMaxLengthCompression;
664 new_piece = std::make_shared<CompressionPiece>(
665 piece->command, length_left, empty, length_left);
666 // MEMCPY
667 for (int i = 0; i < length_left; ++i) {
668 new_piece->argument[i] = piece->argument[i + kMaxLengthCompression];
669 }
670 break;
671 }
673 piece->argument_length = kMaxLengthCompression;
674 unsigned int offset = piece->argument[0] + (piece->argument[1] << 8);
675 new_piece = std::make_shared<CompressionPiece>(
676 piece->command, length_left, piece->argument, piece->argument_length);
677 if (mode == kNintendoMode2) {
678 new_piece->argument[0] =
680 new_piece->argument[1] = (offset + kMaxLengthCompression) >> 8;
681 }
682 if (mode == kNintendoMode1) {
683 new_piece->argument[1] =
685 new_piece->argument[0] = (offset + kMaxLengthCompression) >> 8;
686 }
687 } break;
688 default: {
689 return absl::InvalidArgumentError(
690 "SplitCompressionCommand: Invalid Command");
691 }
692 }
693 return new_piece;
694}
695
697 int mode) {
698 unsigned int pos = 0;
699 auto piece = start;
700 std::vector<uint8_t> output;
701
702 while (piece != nullptr) {
703 if (piece->length <= kMaxLengthNormalHeader) { // Normal header
704 output.push_back(BUILD_HEADER(piece->command, piece->length));
705 pos++;
706 } else {
707 if (piece->length <= kMaxLengthCompression) {
708 output.push_back(kCompressionStringMod |
709 ((uint8_t)piece->command << 2) |
710 (((piece->length - 1) & 0xFF00) >> 8));
711 pos++;
712 printf("Building extended header : cmd: %d, length: %d - %02X\n",
713 piece->command, piece->length, output[pos - 1]);
714 output.push_back(((piece->length - 1) & 0x00FF)); // (char)
715 pos++;
716 } else {
717 // We need to split the command
718 auto new_piece = SplitCompressionPiece(piece, mode);
719 if (!new_piece.ok()) {
720 std::cout << new_piece.status().ToString() << std::endl;
721 }
722 printf("New added piece\n");
723 auto piece_data = new_piece.value();
724 PrintCompressionPiece(piece_data);
725 piece_data->next = piece->next;
726 piece->next = piece_data;
727 continue;
728 }
729 }
730
731 if (piece->command == kCommandRepeatingBytes) {
732 char tmp[2];
733 tmp[0] = piece->argument[0];
734 tmp[1] = piece->argument[1];
735 if (mode == kNintendoMode1) {
736 tmp[0] = piece->argument[1];
737 tmp[1] = piece->argument[0];
738 }
739 for (const auto& each : tmp) {
740 output.push_back(each);
741 pos++;
742 }
743 } else {
744 for (int i = 0; i < piece->argument_length; ++i) {
745 output.push_back(piece->argument[i]);
746 pos++;
747 }
748 }
749 pos += piece->argument_length;
750 piece = piece->next;
751 }
752 output.push_back(kSnesByteMax);
753 return output;
754}
755
757 int mode, int start, int src_data_pos) {
758 if (chain_head->next != nullptr) {
759 Rom temp_rom;
761 temp_rom.LoadFromData(CreateCompressionString(chain_head->next, mode)))
762 ASSIGN_OR_RETURN(auto decomp_data,
763 DecompressV2(temp_rom.data(), 0, temp_rom.size()))
764 if (!std::equal(decomp_data.begin() + start, decomp_data.end(),
765 temp_rom.begin())) {
766 return absl::InternalError(absl::StrFormat(
767 "Compressed data does not match uncompressed data at %d\n",
768 (uint)(src_data_pos - start)));
769 }
770 }
771 return absl::OkStatus();
772}
773
774// Merge consecutive copy if possible
776 CompressionPiecePointer piece = start;
777
778 while (piece != nullptr) {
779 if (piece->command == kCommandDirectCopy && piece->next != nullptr &&
780 piece->next->command == kCommandDirectCopy &&
781 piece->length + piece->next->length <= kMaxLengthCompression) {
782 unsigned int previous_length = piece->length;
783 piece->length = piece->length + piece->next->length;
784
785 for (int i = 0; i < piece->next->argument_length; ++i) {
786 piece->argument[i + previous_length] = piece->next->argument[i];
787 }
788 piece->argument_length = piece->length;
790
791 auto p_next_next = piece->next->next;
792 piece->next = p_next_next;
793 continue; // Next could be another copy
794 }
795 piece = piece->next;
796 }
797 return start;
798}
799
800absl::StatusOr<std::vector<uint8_t>> CompressV2(const uint8_t* data,
801 const int start,
802 const int length, int mode,
803 bool check) {
804 // Surely there's no need to compress zero...
805 if (length == 0) {
806 return std::vector<uint8_t>();
807 }
808
809 // Worst case should be a copy of the string with extended header
810 std::string worst_case = "aaa";
811 auto compressed_chain =
812 std::make_shared<CompressionPiece>(1, 1, worst_case, 2);
813 auto compressed_chain_start = compressed_chain;
814
815 CompressionCommand current_cmd = {/*argument*/ {{}},
816 /*cmd_size*/ {0, 1, 2, 1, 2},
817 /*data_size*/ {0, 0, 0, 0, 0}};
818
819 unsigned int src_pos = start;
820 unsigned int last_pos = start + length - 1;
821 unsigned int comp_accumulator = 0; // Used when skipping using copy
822
823 while (true) {
824 current_cmd.data_size.fill({});
825 current_cmd.arguments.fill({{}});
826
827 CheckByteRepeatV2(data, src_pos, last_pos, current_cmd);
828 CheckWordRepeatV2(data, src_pos, last_pos, current_cmd);
829 CheckIncByteV2(data, src_pos, last_pos, current_cmd);
830 CheckIntraCopyV2(data, src_pos, last_pos, start, current_cmd);
831
832 unsigned int max_win = 2;
833 unsigned int cmd_with_max = kCommandDirectCopy;
834 ValidateForByteGain(current_cmd.data_size, current_cmd.cmd_size, max_win,
835 cmd_with_max);
836 // ValidateForByteGainV2(current_cmd, max_win, cmd_with_max);
837
838 if (cmd_with_max == kCommandDirectCopy) {
839 // This is the worst case scenario
840 // Progress through the next byte, in case there's a different
841 // compression command we can implement before we hit 32 bytes.
842 src_pos++;
843 comp_accumulator++;
844
845 // Arbitrary choice to do a 32 bytes grouping for copy.
846 if (comp_accumulator == 32 || src_pos > last_pos) {
847 std::string buffer = SetBuffer(data, src_pos, comp_accumulator);
848 auto new_comp_piece = std::make_shared<CompressionPiece>(
849 kCommandDirectCopy, comp_accumulator, buffer, comp_accumulator);
850 compressed_chain->next = new_comp_piece;
851 comp_accumulator = 0;
852 }
853 } else {
854 AddAlternativeCompressionCommand(data, compressed_chain, current_cmd,
855 src_pos, comp_accumulator, cmd_with_max,
856 max_win);
857 }
858
859 if (src_pos > last_pos) {
860 printf("Breaking compression loop\n");
861 break;
862 }
863
864 if (check) {
865 RETURN_IF_ERROR(ValidateCompressionResult(compressed_chain_start, mode,
866 start, src_pos))
867 }
868 }
869
870 // Skipping compression chain header
871 MergeCopy(compressed_chain_start->next);
872 PrintCompressionChain(compressed_chain_start);
873 return CreateCompressionString(compressed_chain_start->next, mode);
874}
875
876absl::StatusOr<std::vector<uint8_t>> CompressGraphics(const uint8_t* data,
877 const int pos,
878 const int length) {
879 return CompressV2(data, pos, length, kNintendoMode2);
880}
881
882absl::StatusOr<std::vector<uint8_t>> CompressOverworld(const uint8_t* data,
883 const int pos,
884 const int length) {
885 return CompressV2(data, pos, length, kNintendoMode1);
886}
887
888absl::StatusOr<std::vector<uint8_t>> CompressOverworld(
889 const std::vector<uint8_t> data, const int pos, const int length) {
890 return CompressV3(data, pos, length, kNintendoMode1);
891}
892
894 unsigned int pos = context.src_pos;
895
896 // Ensure the sequence does not start with an uncompressable byte
897 if (pos == 0 || context.data[pos - 1] != context.data[pos]) {
898 char byte_to_repeat = context.data[pos];
899 while (pos <= context.last_pos && context.data[pos] == byte_to_repeat) {
901 pos++;
902 }
903
904 context.current_cmd.arguments[kCommandByteFill][0] = byte_to_repeat;
905
906 // Added debug log
907 DEBUG_LOG("CheckByteRepeatV3: byte_to_repeat = "
908 << (int)byte_to_repeat << ", size = "
910 }
911}
912
914 if (context.src_pos + 1 <= context.last_pos) { // Changed the condition here
915 unsigned int pos = context.src_pos;
916 char byte1 = context.data[pos];
917 char byte2 = context.data[pos + 1];
918 pos += 2;
920 while (pos + 1 <= context.last_pos) {
921 if (context.data[pos] == byte1 && context.data[pos + 1] == byte2)
923 else
924 break;
925 pos += 2;
926 }
927
928 context.current_cmd.arguments[kCommandWordFill][0] = byte1;
929 context.current_cmd.arguments[kCommandWordFill][1] = byte2;
930 }
931
932 DEBUG_LOG("CheckWordRepeatV3: byte1 = "
933 << (int)context.current_cmd.arguments[kCommandWordFill][0]
934 << ", byte2 = "
935 << (int)context.current_cmd.arguments[kCommandWordFill][1]
936 << ", size = " << context.current_cmd.data_size[kCommandWordFill]);
937}
938
940 unsigned int pos = context.src_pos;
941 uint8_t byte = context.data[pos];
942 pos++;
944 byte++;
945
946 while (pos <= context.last_pos && byte == context.data[pos]) {
948 byte++;
949 pos++;
950 }
951
952 // Let's see if the sequence is surrounded by identical bytes and if so,
953 // consider if a direct copy is better.
954 if (context.current_cmd.data_size[kCommandIncreasingFill] == 3 &&
955 context.src_pos > 0 && pos < context.data.size() &&
956 context.data[context.src_pos - 1] == context.data[pos]) {
958 0; // Reset the size to 0 to prioritize direct copy
959 return;
960 }
961
963 context.data[context.src_pos];
964
965 DEBUG_LOG("CheckIncByteV3: byte = "
967 << ", size = "
969}
970
972 const int window_size =
973 32; // This can be adjusted for optimal performance and results
974
975 // We'll only search for repeating sequences if we're not at the very
976 // beginning
977 if (context.src_pos > 0 &&
978 context.src_pos + window_size <= context.data.size()) {
979 unsigned int max_copied_size = 0;
980 unsigned int best_search_start = 0;
981
982 // Slide the window over the source data
983 for (int win_pos = 1; win_pos < window_size && win_pos < context.src_pos;
984 ++win_pos) {
985 auto start_search_from = context.data.begin() + context.src_pos - win_pos;
986 auto search_end = context.data.begin() + context.src_pos;
987
988 // Use std::search to find the sequence in the window in the previous
989 // source data
990 auto found_pos = std::search(
991 start_search_from, search_end, context.data.begin() + context.src_pos,
992 context.data.begin() + context.src_pos + win_pos);
993
994 if (found_pos != search_end) {
995 // Check the entire length of the match
996 unsigned int len = 0;
997 while (context.src_pos + len < context.data.size() &&
998 context.data[context.src_pos + len] == *(found_pos + len)) {
999 len++;
1000 }
1001
1002 if (len > max_copied_size) {
1003 max_copied_size = len;
1004 best_search_start = found_pos - context.data.begin();
1005 }
1006 }
1007 }
1008
1009 if (max_copied_size >
1011 DEBUG_LOG("CheckIntraCopyV3: Detected repeating sequence of length "
1012 << max_copied_size << " starting from " << best_search_start);
1013 context.current_cmd.data_size[kCommandRepeatingBytes] = max_copied_size;
1015 best_search_start & kSnesByteMax;
1017 best_search_start >> 8;
1018 }
1019
1020 DEBUG_LOG("CheckIntraCopyV3: max_copied_size = " << max_copied_size
1021 << ", best_search_start = "
1022 << best_search_start);
1023 }
1024}
1025
1027 // Initialize the current_cmd with default values.
1028 context.current_cmd = {/*argument*/ {{}},
1029 /*cmd_size*/ {0, 1, 2, 1, 2},
1030 /*data_size*/ {0, 0, 0, 0, 0}};
1031}
1032
1034 // Reset the data_size and arguments for a fresh check.
1035 context.current_cmd.data_size.fill({});
1036 context.current_cmd.arguments.fill({{}});
1037
1038 CheckByteRepeatV3(context);
1039 CheckWordRepeatV3(context);
1040 CheckIncByteV3(context);
1041 CheckIntraCopyV3(context);
1042
1043 DEBUG_LOG("CheckAvailableCompressionCommands: src_pos = " << context.src_pos);
1044}
1045
1047 int max_net_savings = -1; // Adjusted the bias to consider any savings
1048
1049 // Start with the default scenario.
1051
1052 for (unsigned int cmd_i = 1; cmd_i < 5; cmd_i++) {
1053 unsigned int cmd_size_taken = context.current_cmd.data_size[cmd_i];
1054 int net_savings = cmd_size_taken - context.current_cmd.cmd_size[cmd_i];
1055
1056 // Skip commands that aren't efficient.
1057 if (cmd_size_taken <= 2 && cmd_i != kCommandDirectCopy) {
1058 continue;
1059 }
1060
1061 // Check surrounding data for optimization.
1062 if (context.src_pos > 0 &&
1063 context.src_pos + cmd_size_taken < context.data.size()) {
1064 char prev_byte = context.data[context.src_pos - 1];
1065 char next_byte = context.data[context.src_pos + cmd_size_taken];
1066 if (prev_byte != next_byte && cmd_size_taken == 3) {
1067 continue;
1068 }
1069 }
1070
1071 // Check if the current command offers more net savings.
1072 if (net_savings > max_net_savings) {
1073 context.cmd_with_max = cmd_i;
1074 max_net_savings = net_savings;
1075 }
1076 }
1077
1078 DEBUG_LOG("DetermineBestCompression: cmd_with_max = "
1079 << context.cmd_with_max << ", data_size = "
1080 << context.current_cmd.data_size[context.cmd_with_max]);
1081}
1082
1084 // If the next best compression method isn't direct copy and we have bytes
1085 // accumulated for direct copy, flush them out.
1086 if (context.cmd_with_max != kCommandDirectCopy &&
1087 context.comp_accumulator > 0) {
1088 uint8_t header = BUILD_HEADER(kCommandDirectCopy, context.comp_accumulator);
1089 context.compressed_data.push_back(header);
1090 std::vector<uint8_t> uncompressed_data(
1091 context.data.begin() + context.src_pos - context.comp_accumulator,
1092 context.data.begin() + context.src_pos);
1093 context.compressed_data.insert(context.compressed_data.end(),
1094 uncompressed_data.begin(),
1095 uncompressed_data.end());
1096 context.comp_accumulator = 0;
1097 return;
1098 }
1099
1100 // If the next best compression method is not direct copy and we haven't
1101 // accumulated any bytes, treat it as a single byte direct copy.
1102 if (context.cmd_with_max != kCommandDirectCopy &&
1103 context.comp_accumulator == 0) {
1104 context.compressed_data.push_back(
1105 0x00); // Command for a single byte direct copy
1106 context.compressed_data.push_back(
1107 context.data[context.src_pos]); // The single byte
1108 context.src_pos++;
1109 return;
1110 }
1111
1112 // If we reach here, accumulate bytes for a direct copy.
1113 context.src_pos++;
1114 context.comp_accumulator++;
1115
1116 // If we've accumulated the maximum bytes for a direct copy command or
1117 // reached the end, flush them.
1118 if (context.comp_accumulator >= 32 || context.src_pos > context.last_pos) {
1119 uint8_t header = BUILD_HEADER(kCommandDirectCopy, context.comp_accumulator);
1120 context.compressed_data.push_back(header);
1121 std::vector<uint8_t> uncompressed_data(
1122 context.data.begin() + context.src_pos - context.comp_accumulator,
1123 context.data.begin() + context.src_pos);
1124 context.compressed_data.insert(context.compressed_data.end(),
1125 uncompressed_data.begin(),
1126 uncompressed_data.end());
1127 context.comp_accumulator = 0;
1128 }
1129
1130 DEBUG_LOG("HandleDirectCopy: src_pos = " << context.src_pos
1131 << ", compressed_data size = "
1132 << context.compressed_data.size());
1133}
1134
1136 DEBUG_LOG("AddCompressionToChain: Adding command arguments: ");
1137
1138 // If there's uncompressed data, add a copy chunk before the compression
1139 // command
1140 if (context.comp_accumulator != 0) {
1141 uint8_t header = BUILD_HEADER(kCommandDirectCopy, context.comp_accumulator);
1142 context.compressed_data.push_back(header);
1143 std::vector<uint8_t> uncompressed_data(
1144 context.data.begin() + context.src_pos - context.comp_accumulator,
1145 context.data.begin() + context.src_pos);
1146 context.compressed_data.insert(context.compressed_data.end(),
1147 uncompressed_data.begin(),
1148 uncompressed_data.end());
1149 context.comp_accumulator = 0;
1150 }
1151
1152 // Now, add the compression command
1153 uint8_t header =
1154 BUILD_HEADER(context.cmd_with_max,
1155 context.current_cmd.data_size[context.cmd_with_max]);
1156 context.compressed_data.push_back(header);
1157
1158 DEBUG_LOG("AddCompressionToChain: (Before) src_pos = "
1159 << context.src_pos
1160 << ", compressed_data size = " << context.compressed_data.size());
1161
1162 // Add the command arguments to the compressed_data vector
1163 context.compressed_data.push_back(
1164 context.current_cmd.arguments[context.cmd_with_max][0]);
1165 if (context.current_cmd.cmd_size[context.cmd_with_max] == 2) {
1166 context.compressed_data.push_back(
1167 context.current_cmd.arguments[context.cmd_with_max][1]);
1168 }
1169
1170 context.src_pos += context.current_cmd.data_size[context.cmd_with_max];
1171 context.comp_accumulator = 0;
1172
1173 DEBUG_LOG("AddCompressionToChain: (After) src_pos = "
1174 << context.src_pos
1175 << ", compressed_data size = " << context.compressed_data.size());
1176}
1177
1179 if (!context.compressed_data.empty()) {
1180 Rom temp_rom;
1181 RETURN_IF_ERROR(temp_rom.LoadFromData(context.compressed_data));
1182 ASSIGN_OR_RETURN(auto decomp_data,
1183 DecompressV2(temp_rom.data(), 0, temp_rom.size()))
1184
1185 if (!std::equal(decomp_data.begin() + context.start, decomp_data.end(),
1186 temp_rom.begin())) {
1187 return absl::InternalError(absl::StrFormat(
1188 "Compressed data does not match uncompressed data at %d\n",
1189 (context.src_pos - context.start)));
1190 }
1191 }
1192 return absl::OkStatus();
1193}
1194
1195absl::StatusOr<CompressionPiece> SplitCompressionPieceV3(
1196 CompressionPiece& piece, int mode) {
1197 CompressionPiece new_piece;
1198 unsigned int length_left = piece.length - kMaxLengthCompression;
1200
1201 switch (piece.command) {
1202 case kCommandByteFill:
1203 case kCommandWordFill:
1204 new_piece = CompressionPiece(piece.command, length_left, piece.argument,
1205 piece.argument_length);
1206 break;
1208 new_piece = CompressionPiece(piece.command, length_left, piece.argument,
1209 piece.argument_length);
1210 new_piece.argument[0] = (char)(piece.argument[0] + kMaxLengthCompression);
1211 break;
1212 case kCommandDirectCopy: {
1214 std::string empty_string = "";
1215 new_piece = CompressionPiece(piece.command, length_left, empty_string,
1216 length_left);
1217 // MEMCPY
1218 for (int i = 0; i < length_left; ++i) {
1219 new_piece.argument[i] = piece.argument[i + kMaxLengthCompression];
1220 }
1221 break;
1222 }
1225 unsigned int offset = piece.argument[0] + (piece.argument[1] << 8);
1226 new_piece = CompressionPiece(piece.command, length_left, piece.argument,
1227 piece.argument_length);
1228 if (mode == kNintendoMode2) {
1229 new_piece.argument[0] = (offset + kMaxLengthCompression) & kSnesByteMax;
1230 new_piece.argument[1] = (offset + kMaxLengthCompression) >> 8;
1231 }
1232 if (mode == kNintendoMode1) {
1233 new_piece.argument[1] = (offset + kMaxLengthCompression) & kSnesByteMax;
1234 new_piece.argument[0] = (offset + kMaxLengthCompression) >> 8;
1235 }
1236 } break;
1237 default: {
1238 return absl::InvalidArgumentError(
1239 "SplitCompressionCommand: Invalid Command");
1240 }
1241 }
1242
1243 return new_piece;
1244}
1245
1247 unsigned int pos = 0;
1248
1249 for (CompressionPiece& piece : context.compression_pieces) {
1250 if (piece.length <= kMaxLengthNormalHeader) { // Normal Header
1251 context.compression_string.push_back(
1252 BUILD_HEADER(piece.command, piece.length));
1253 pos++;
1254 } else {
1255 if (piece.length <= kMaxLengthCompression) {
1256 context.compression_string.push_back(
1257 kCompressionStringMod | ((uint8_t)piece.command << 2) |
1258 (((piece.length - 1) & 0xFF00) >> 8));
1259 pos++;
1260 std::cout << "Building extended header : cmd: " << piece.command
1261 << ", length: " << piece.length << " - "
1262 << (int)context.compression_string[pos - 1] << std::endl;
1263 context.compression_string.push_back(
1264 ((piece.length - 1) & 0x00FF)); // (char)
1265 } else {
1266 // We need to split the command
1267 auto new_piece = SplitCompressionPieceV3(piece, context.mode);
1268 if (!new_piece.ok()) {
1269 std::cout << new_piece.status().ToString() << std::endl;
1270 }
1271 context.compression_pieces.insert(
1272 context.compression_pieces.begin() + pos + 1, new_piece.value());
1273 continue;
1274 }
1275 }
1276
1277 if (piece.command == kCommandRepeatingBytes) {
1278 char tmp[2];
1279 tmp[0] = piece.argument[0];
1280 tmp[1] = piece.argument[1];
1281 if (context.mode == kNintendoMode1) {
1282 tmp[0] = piece.argument[1];
1283 tmp[1] = piece.argument[0];
1284 }
1285 for (const auto& each : tmp) {
1286 context.compression_string.push_back(each);
1287 pos++;
1288 }
1289 } else {
1290 for (int i = 0; i < piece.argument_length; ++i) {
1291 context.compression_string.push_back(piece.argument[i]);
1292 pos++;
1293 }
1294 }
1295 pos += piece.argument_length;
1296 }
1297
1298 // Add any remaining uncompressed data
1299 if (context.comp_accumulator > 0) {
1300 context.compressed_data.insert(
1301 context.compressed_data.end(),
1302 context.data.begin() + context.src_pos - context.comp_accumulator,
1303 context.data.begin() + context.src_pos);
1304 context.comp_accumulator = 0;
1305 }
1306
1307 // Add the end marker to the compressed data
1308 context.compressed_data.push_back(kSnesByteMax);
1309 DEBUG_LOG("FinalizeCompression: compressed_data size = "
1310 << context.compressed_data.size());
1311}
1312
1313absl::StatusOr<std::vector<uint8_t>> CompressV3(
1314 const std::vector<uint8_t>& data, const int start, const int length,
1315 int mode, bool check) {
1316 if (length == 0) {
1317 return std::vector<uint8_t>();
1318 }
1319
1320 CompressionContext context(data, start, length, mode);
1321 InitializeCompression(context);
1322
1323 while (context.src_pos <= context.last_pos) {
1325 DetermineBestCompression(context);
1326
1327 DEBUG_LOG("CompressV3 Loop: cmd_with_max = " << context.cmd_with_max);
1328
1329 if (context.cmd_with_max == kCommandDirectCopy) {
1330 HandleDirectCopy(context);
1331 } else {
1332 AddCompressionToChain(context);
1333 }
1334
1335 if (check) {
1337 }
1338 }
1339
1340 FinalizeCompression(context);
1341 return std::vector<uint8_t>(context.compressed_data.begin(),
1342 context.compressed_data.end());
1343}
1344
1345std::string SetBuffer(const uint8_t* data, int src_pos, int comp_accumulator) {
1346 std::string buffer;
1347 for (int i = 0; i < comp_accumulator; ++i) {
1348 buffer.push_back(data[i + src_pos - comp_accumulator]);
1349 }
1350 return buffer;
1351}
1352
1353std::string SetBuffer(const std::vector<uint8_t>& data, int src_pos,
1354 int comp_accumulator) {
1355 std::string buffer;
1356 for (int i = 0; i < comp_accumulator; ++i) {
1357 buffer.push_back(data[i + src_pos - comp_accumulator]);
1358 }
1359 return buffer;
1360}
1361
1362void memfill(const uint8_t* data, std::vector<uint8_t>& buffer, int buffer_pos,
1363 int offset, int length) {
1364 auto a = data[offset];
1365 auto b = data[offset + 1];
1366 for (int i = 0; i < length; i = i + 2) {
1367 buffer[buffer_pos + i] = a;
1368 if ((i + 1) < length) buffer[buffer_pos + i + 1] = b;
1369 }
1370}
1371
1372absl::StatusOr<std::vector<uint8_t>> DecompressV2(const uint8_t* data,
1373 int offset, int size,
1374 int mode) {
1375 if (size == 0) {
1376 return std::vector<uint8_t>();
1377 }
1378
1379 std::vector<uint8_t> buffer(size, 0);
1380 unsigned int length = 0;
1381 unsigned int buffer_pos = 0;
1382 uint8_t command = 0;
1383 uint8_t header = data[offset];
1384
1385 while (header != kSnesByteMax) {
1386 if ((header & kExpandedMod) == kExpandedMod) {
1387 // Expanded Command
1388 command = ((header >> 2) & kCommandMod);
1389 length = (((header << 8) | data[offset + 1]) & kExpandedLengthMod);
1390 offset += 2; // Advance 2 bytes in ROM
1391 } else {
1392 // Normal Command
1393 command = ((header >> 5) & kCommandMod);
1394 length = (header & kNormalLengthMod);
1395 offset += 1; // Advance 1 byte in ROM
1396 }
1397 length += 1; // each commands is at least of size 1 even if index 00
1398
1399 switch (command) {
1400 case kCommandDirectCopy: // Does not advance in the ROM
1401 memcpy(buffer.data() + buffer_pos, data + offset, length);
1402 buffer_pos += length;
1403 offset += length;
1404 break;
1405 case kCommandByteFill:
1406 memset(buffer.data() + buffer_pos, (int)(data[offset]), length);
1407 buffer_pos += length;
1408 offset += 1; // Advances 1 byte in the ROM
1409 break;
1410 case kCommandWordFill:
1411 memfill(data, buffer, buffer_pos, offset, length);
1412 buffer_pos += length;
1413 offset += 2; // Advance 2 byte in the ROM
1414 break;
1416 auto inc_byte = data[offset];
1417 for (int i = 0; i < length; i++) {
1418 buffer[buffer_pos] = inc_byte++;
1419 buffer_pos++;
1420 }
1421 offset += 1; // Advance 1 byte in the ROM
1422 } break;
1424 uint16_t s1 = ((data[offset + 1] & kSnesByteMax) << 8);
1425 uint16_t s2 = (data[offset] & kSnesByteMax);
1426 int addr = (s1 | s2);
1427 if (mode == kNintendoMode1) { // Reversed byte order for
1428 // overworld maps
1429 addr = (data[offset + 1] & kSnesByteMax) |
1430 ((data[offset] & kSnesByteMax) << 8);
1431 }
1432 if (addr > offset) {
1433 return absl::InternalError(
1434 absl::StrFormat("Decompress: Offset for command copy exceeds "
1435 "current position "
1436 "(Offset : %#04x | Pos : %#06x)\n",
1437 addr, offset));
1438 }
1439 if (buffer_pos + length >= size) {
1440 size *= 2;
1441 buffer.resize(size);
1442 }
1443 memcpy(buffer.data() + buffer_pos, buffer.data() + addr, length);
1444 buffer_pos += length;
1445 offset += 2;
1446 } break;
1447 default: {
1448 std::cout << absl::StrFormat(
1449 "Decompress: Invalid header (Offset : %#06x, Command: %#04x)\n",
1450 offset, command);
1451 } break;
1452 }
1453 // check next byte
1454 header = data[offset];
1455 }
1456
1457 return buffer;
1458}
1459
1460absl::StatusOr<std::vector<uint8_t>> DecompressGraphics(const uint8_t* data,
1461 int pos, int size) {
1462 return DecompressV2(data, pos, size, kNintendoMode2);
1463}
1464
1465absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(const uint8_t* data,
1466 int pos, int size) {
1467 return DecompressV2(data, pos, size, kNintendoMode1);
1468}
1469
1470absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(
1471 const std::vector<uint8_t> data, int pos, int size) {
1472 return DecompressV2(data.data(), pos, size, kNintendoMode1);
1473}
1474
1475} // namespace lc_lz2
1476} // namespace gfx
1477} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:59
auto begin()
Definition rom.h:166
absl::Status LoadFromData(const std::vector< uint8_t > &data, bool z3_load=true)
Definition rom.cc:218
auto data() const
Definition rom.h:164
auto size() const
Definition rom.h:163
#define DEBUG_LOG(msg)
#define BUILD_HEADER(command, length)
Definition compression.h:13
#define RETURN_IF_ERROR(expression)
Definition macro.h:62
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition macro.h:70
Contains the LC_LZ2 compression algorithm.
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
Definition compression.h:58
constexpr int kCommandDirectCopy
Definition compression.h:45
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)
struct CompressionPiece CompressionPiece
Definition compression.h:89
absl::StatusOr< std::vector< uint8_t > > CompressOverworld(const uint8_t *data, const int pos, const int length)
constexpr int kSnesByteMax
Definition compression.h:55
constexpr int kNintendoMode1
Definition compression.h:53
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
Definition compression.h:90
void CheckIncByteV3(CompressionContext &context)
absl::Status ValidateCompressionResultV3(const CompressionContext &context)
constexpr int kNintendoMode2
Definition compression.h:54
std::string SetBuffer(const uint8_t *data, int src_pos, int comp_accumulator)
constexpr int kCommandRepeatingBytes
Definition compression.h:49
std::array< std::array< char, 2 >, 5 > CommandArgumentArray
Definition compression.h:74
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
Definition compression.h:56
void PrintCompressionPiece(const CompressionPiecePointer &piece)
constexpr int kCommandWordFill
Definition compression.h:47
constexpr int kMaxLengthNormalHeader
Definition compression.h:51
void CheckAvailableCompressionCommands(CompressionContext &context)
constexpr int kMaxLengthCompression
Definition compression.h:52
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
Definition compression.h:76
constexpr int kCommandIncreasingFill
Definition compression.h:48
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
Definition compression.h:46
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
Definition compression.h:57
const std::array< int, 5 > kCommandSizes
absl::StatusOr< std::vector< uint8_t > > DecompressV2(const uint8_t *data, int offset, int size, int mode)
Decompresses a buffer of data using the LC_LZ2 algorithm.
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
Definition compression.h:59
constexpr int kCompressionStringMod
Definition compression.h:60
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
Definition compression.h:75
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)
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)
Main namespace for the application.
Definition controller.cc:18
std::array< std::array< char, 2 >, 5 > arguments
Definition compression.h:65
std::vector< CompressionPiece > compression_pieces
std::vector< uint8_t > compression_string
std::vector< uint8_t > compressed_data