yaze 0.2.0
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 "absl/strings/str_cat.h"
10#include "app/core/constants.h"
11#include "app/rom.h"
12
13#define DEBUG_LOG(msg) std::cout << msg << std::endl
14
15namespace yaze {
16namespace app {
17namespace gfx {
18
19namespace lc_lz2 {
20
21// Compression commands
22
24 std::cout << "Command: " << std::to_string(piece->command) << "\n";
25 std::cout << "Command Length: " << piece->length << "\n";
26 std::cout << "Argument: ";
27 auto arg_size = piece->argument.size();
28 for (int i = 0; i < arg_size; ++i) {
29 printf("%02X ", piece->argument.at(i));
30 }
31 std::cout << "\nArgument Length: " << piece->argument_length << "\n";
32}
33
35 auto compressed_chain = chain_head->next;
36 while (compressed_chain != nullptr) {
37 std::cout << "- Compression Piece -\n";
38 PrintCompressionPiece(compressed_chain);
39 compressed_chain = compressed_chain->next;
40 }
41}
42
43void CheckByteRepeat(const uchar* rom_data, DataSizeArray& data_size_taken,
44 CommandArgumentArray& cmd_args, uint& src_data_pos,
45 const uint last_pos) {
46 uint pos = src_data_pos;
47 char byte_to_repeat = rom_data[pos];
48 while (pos <= last_pos && rom_data[pos] == byte_to_repeat) {
49 data_size_taken[kCommandByteFill]++;
50 pos++;
51 }
52 cmd_args[kCommandByteFill][0] = byte_to_repeat;
53}
54
55void CheckWordRepeat(const uchar* rom_data, DataSizeArray& data_size_taken,
56 CommandArgumentArray& cmd_args, uint& src_data_pos,
57 const uint last_pos) {
58 if (src_data_pos + 2 <= last_pos &&
59 rom_data[src_data_pos] != rom_data[src_data_pos + 1]) {
60 uint pos = src_data_pos;
61 char byte1 = rom_data[pos];
62 char byte2 = rom_data[pos + 1];
63 pos += 2;
64 data_size_taken[kCommandWordFill] = 2;
65 while (pos + 1 <= last_pos) {
66 if (rom_data[pos] == byte1 && rom_data[pos + 1] == byte2)
67 data_size_taken[kCommandWordFill] += 2;
68 else
69 break;
70 pos += 2;
71 }
72 cmd_args[kCommandWordFill][0] = byte1;
73 cmd_args[kCommandWordFill][1] = byte2;
74 }
75}
76
77void CheckIncByte(const uchar* rom_data, DataSizeArray& data_size_taken,
78 CommandArgumentArray& cmd_args, uint& src_data_pos,
79 const uint last_pos) {
80 uint pos = src_data_pos;
81 char byte = rom_data[pos];
82 pos++;
83 data_size_taken[kCommandIncreasingFill] = 1;
84 byte++;
85 while (pos <= last_pos && byte == rom_data[pos]) {
86 data_size_taken[kCommandIncreasingFill]++;
87 byte++;
88 pos++;
89 }
90 cmd_args[kCommandIncreasingFill][0] = rom_data[src_data_pos];
91}
92
93void CheckIntraCopy(const uchar* rom_data, DataSizeArray& data_size_taken,
94 CommandArgumentArray& cmd_args, uint& src_data_pos,
95 const uint last_pos, uint start) {
96 if (src_data_pos != start) {
97 uint searching_pos = start;
98 uint current_pos_u = src_data_pos;
99 uint copied_size = 0;
100 uint search_start = start;
101
102 while (searching_pos < src_data_pos && current_pos_u <= last_pos) {
103 while (rom_data[current_pos_u] != rom_data[searching_pos] &&
104 searching_pos < src_data_pos)
105 searching_pos++;
106 search_start = searching_pos;
107 while (current_pos_u <= last_pos &&
108 rom_data[current_pos_u] == rom_data[searching_pos] &&
109 searching_pos < src_data_pos) {
110 copied_size++;
111 current_pos_u++;
112 searching_pos++;
113 }
114 if (copied_size > data_size_taken[kCommandRepeatingBytes]) {
115 search_start -= start;
116 printf("- Found repeat of %d at %d\n", copied_size, search_start);
117 data_size_taken[kCommandRepeatingBytes] = copied_size;
118 cmd_args[kCommandRepeatingBytes][0] = search_start & kSnesByteMax;
119 cmd_args[kCommandRepeatingBytes][1] = search_start >> 8;
120 }
121 current_pos_u = src_data_pos;
122 copied_size = 0;
123 }
124 }
125}
126
127// Check if a command managed to pick up `max_win` or more bytes
128// Avoids being even with copy command, since it's possible to merge copy
129void ValidateForByteGain(const DataSizeArray& data_size_taken,
130 const CommandSizeArray& cmd_size, uint& max_win,
131 uint& cmd_with_max) {
132 for (uint cmd_i = 1; cmd_i < 5; cmd_i++) {
133 uint cmd_size_taken = data_size_taken[cmd_i];
134 // TODO(@scawful): Replace conditional with table of command sizes
135 // "Table that is even with copy but all other cmd are 2"
136 auto table_check =
137 !(cmd_i == kCommandRepeatingBytes && cmd_size_taken == 3);
138 if (cmd_size_taken > max_win && cmd_size_taken > cmd_size[cmd_i] &&
139 table_check) {
140 printf("==> C:%d / S:%d\n", cmd_i, cmd_size_taken);
141 cmd_with_max = cmd_i;
142 max_win = cmd_size_taken;
143 }
144 }
145}
146
148 CompressionPiecePointer& compressed_chain,
149 const CommandSizeArray& cmd_size,
150 const CommandArgumentArray& cmd_args,
151 uint& src_data_pos, uint& comp_accumulator,
152 uint& cmd_with_max, uint& max_win) {
153 printf("- Ok we get a gain from %d\n", cmd_with_max);
154 std::string buffer;
155 buffer.push_back(cmd_args[cmd_with_max][0]);
156 if (cmd_size[cmd_with_max] == 2) {
157 buffer.push_back(cmd_args[cmd_with_max][1]);
158 }
159
160 auto new_comp_piece = std::make_shared<CompressionPiece>(
161 cmd_with_max, max_win, buffer, cmd_size[cmd_with_max]);
162 PrintCompressionPiece(new_comp_piece);
163 // If we let non compressed stuff, we need to add a copy chunk before
164 if (comp_accumulator != 0) {
165 std::string copy_buff;
166 copy_buff.resize(comp_accumulator);
167 for (int i = 0; i < comp_accumulator; ++i) {
168 copy_buff[i] = rom_data[i + src_data_pos - comp_accumulator];
169 }
170 auto copy_chunk = std::make_shared<CompressionPiece>(
171 kCommandDirectCopy, comp_accumulator, copy_buff, comp_accumulator);
172 compressed_chain->next = copy_chunk;
173 compressed_chain = copy_chunk;
174 } else {
175 compressed_chain->next = new_comp_piece;
176 compressed_chain = new_comp_piece;
177 }
178 src_data_pos += max_win;
179 comp_accumulator = 0;
180}
181
182// ============================================================================
183// Compression V2
184
185void CheckByteRepeatV2(const uchar* data, uint& src_pos, const uint last_pos,
186 CompressionCommand& cmd) {
187 uint i = 0;
188 while (src_pos + i < last_pos && data[src_pos] == data[src_pos + i]) {
189 ++i;
190 }
192 cmd.arguments[kCommandByteFill][0] = data[src_pos];
193}
194
195void CheckWordRepeatV2(const uchar* data, uint& src_pos, const uint last_pos,
196 CompressionCommand& cmd) {
197 if (src_pos + 2 <= last_pos && data[src_pos] != data[src_pos + 1]) {
198 uint pos = src_pos;
199 char byte1 = data[pos];
200 char byte2 = data[pos + 1];
201 pos += 2;
203 while (pos + 1 <= last_pos) {
204 if (data[pos] == byte1 && data[pos + 1] == byte2)
205 cmd.data_size[kCommandWordFill] += 2;
206 else
207 break;
208 pos += 2;
209 }
210 cmd.arguments[kCommandWordFill][0] = byte1;
211 cmd.arguments[kCommandWordFill][1] = byte2;
212 }
213}
214
215void CheckIncByteV2(const uchar* rom_data, uint& src_data_pos,
216 const uint last_pos, CompressionCommand& cmd) {
217 uint pos = src_data_pos;
218 char byte = rom_data[pos];
219 pos++;
221 byte++;
222 while (pos <= last_pos && byte == rom_data[pos]) {
224 byte++;
225 pos++;
226 }
227 cmd.arguments[kCommandIncreasingFill][0] = rom_data[src_data_pos];
228}
229
230void CheckIntraCopyV2(const uchar* rom_data, uint& src_data_pos,
231 const uint last_pos, uint start,
232 CompressionCommand& cmd) {
233 if (src_data_pos != start) {
234 uint searching_pos = start;
235 uint current_pos_u = src_data_pos;
236 uint copied_size = 0;
237 uint search_start = start;
238
239 while (searching_pos < src_data_pos && current_pos_u <= last_pos) {
240 while (rom_data[current_pos_u] != rom_data[searching_pos] &&
241 searching_pos < src_data_pos)
242 searching_pos++;
243 search_start = searching_pos;
244 while (current_pos_u <= last_pos &&
245 rom_data[current_pos_u] == rom_data[searching_pos] &&
246 searching_pos < src_data_pos) {
247 copied_size++;
248 current_pos_u++;
249 searching_pos++;
250 }
251 if (copied_size > cmd.data_size[kCommandRepeatingBytes]) {
252 search_start -= start;
253 printf("- Found repeat of %d at %d\n", copied_size, search_start);
254 cmd.data_size[kCommandRepeatingBytes] = copied_size;
255 cmd.arguments[kCommandRepeatingBytes][0] = search_start & kSnesByteMax;
256 cmd.arguments[kCommandRepeatingBytes][1] = search_start >> 8;
257 }
258 current_pos_u = src_data_pos;
259 copied_size = 0;
260 }
261 }
262}
263
264// Table indicating command sizes, in bytes
265const std::array<int, 5> kCommandSizes = {1, 2, 2, 2, 3};
266
267// TODO(@scawful): TEST ME
269 uint& cmd_with_max) {
270 for (uint cmd_i = 1; cmd_i < 5; cmd_i++) {
271 uint cmd_size_taken = cmd.data_size[cmd_i];
272 // Check if the command size exceeds the maximum win and the size in the
273 // command sizes table, except for the repeating bytes command when the size
274 // taken is 3
275 if (cmd_size_taken > max_win && cmd_size_taken > kCommandSizes[cmd_i] &&
276 !(cmd_i == kCommandRepeatingBytes && cmd_size_taken == 3)) {
277 printf("==> C:%d / S:%d\n", cmd_i, cmd_size_taken);
278 cmd_with_max = cmd_i;
279 max_win = cmd_size_taken;
280 }
281 }
282}
283
285 const CompressionCommand& cmd,
286 CompressionPiecePointer& compressed_chain,
287 uint& src_data_pos, uint& comp_accumulator,
288 uint& cmd_with_max, uint& max_win) {
289 printf("- Ok we get a gain from %d\n", cmd_with_max);
290 std::string buffer;
291 buffer.push_back(cmd.arguments[cmd_with_max][0]);
292 if (cmd.cmd_size[cmd_with_max] == 2) {
293 buffer.push_back(cmd.arguments[cmd_with_max][1]);
294 }
295
296 auto new_comp_piece = std::make_shared<CompressionPiece>(
297 cmd_with_max, max_win, buffer, cmd.cmd_size[cmd_with_max]);
298 PrintCompressionPiece(new_comp_piece);
299 // If we let non compressed stuff, we need to add a copy chunk before
300 if (comp_accumulator != 0) {
301 std::string copy_buff;
302 copy_buff.resize(comp_accumulator);
303 for (int i = 0; i < comp_accumulator; ++i) {
304 copy_buff[i] = rom_data[i + src_data_pos - comp_accumulator];
305 }
306 auto copy_chunk = std::make_shared<CompressionPiece>(
307 kCommandDirectCopy, comp_accumulator, copy_buff, comp_accumulator);
308 compressed_chain->next = copy_chunk;
309 compressed_chain = copy_chunk;
310 } else {
311 compressed_chain->next = new_comp_piece;
312 compressed_chain = new_comp_piece;
313 }
314 src_data_pos += max_win;
315 comp_accumulator = 0;
316}
317
319 const uchar* rom_data, CompressionPiecePointer& compressed_chain,
320 const CompressionCommand& command, uint& source_data_position,
321 uint& uncompressed_data_size, uint& best_command, uint& best_command_gain) {
322 std::cout << "- Identified a gain from command: " << best_command
323 << std::endl;
324
325 // Create a buffer to store the arguments for the best command.
326 std::string argument_buffer;
327 argument_buffer.push_back(command.arguments[best_command][0]);
328 if (command.cmd_size[best_command] == 2) {
329 argument_buffer.push_back(command.arguments[best_command][1]);
330 }
331
332 // Create a new compression piece for the best command.
333 auto new_compression_piece = std::make_shared<CompressionPiece>(
334 best_command, best_command_gain, argument_buffer,
335 command.cmd_size[best_command]);
336 PrintCompressionPiece(new_compression_piece);
337
338 // If there is uncompressed data, create a direct copy compression piece for
339 // it.
340 if (uncompressed_data_size != 0) {
341 std::string copy_buffer(uncompressed_data_size, 0);
342 for (int i = 0; i < uncompressed_data_size; ++i) {
343 copy_buffer[i] =
344 rom_data[i + source_data_position - uncompressed_data_size];
345 }
346 auto direct_copy_piece = std::make_shared<CompressionPiece>(
347 kCommandDirectCopy, uncompressed_data_size, copy_buffer,
348 uncompressed_data_size);
349
350 // Append the direct copy piece to the chain.
351 compressed_chain->next = direct_copy_piece;
352 compressed_chain = direct_copy_piece;
353 }
354
355 // Append the new compression piece to the chain.
356 compressed_chain->next = new_compression_piece;
357 compressed_chain = new_compression_piece;
358
359 // Update the position in the source data and reset the uncompressed data
360 // size.
361 source_data_position += best_command_gain;
362 uncompressed_data_size = 0;
363}
364
365absl::StatusOr<CompressionPiecePointer> SplitCompressionPiece(
366 CompressionPiecePointer& piece, int mode) {
367 CompressionPiecePointer new_piece;
368 uint length_left = piece->length - kMaxLengthCompression;
369 piece->length = kMaxLengthCompression;
370
371 switch (piece->command) {
372 case kCommandByteFill:
373 case kCommandWordFill:
374 new_piece = std::make_shared<CompressionPiece>(
375 piece->command, length_left, piece->argument, piece->argument_length);
376 break;
378 new_piece = std::make_shared<CompressionPiece>(
379 piece->command, length_left, piece->argument, piece->argument_length);
380 new_piece->argument[0] =
381 (char)(piece->argument[0] + kMaxLengthCompression);
382 break;
384 piece->argument_length = kMaxLengthCompression;
385 new_piece = std::make_shared<CompressionPiece>(
386 piece->command, length_left, nullptr, length_left);
387 // MEMCPY
388 for (int i = 0; i < length_left; ++i) {
389 new_piece->argument[i] = piece->argument[i + kMaxLengthCompression];
390 }
391 break;
393 piece->argument_length = kMaxLengthCompression;
394 uint offset = piece->argument[0] + (piece->argument[1] << 8);
395 new_piece = std::make_shared<CompressionPiece>(
396 piece->command, length_left, piece->argument, piece->argument_length);
397 if (mode == kNintendoMode2) {
398 new_piece->argument[0] =
400 new_piece->argument[1] = (offset + kMaxLengthCompression) >> 8;
401 }
402 if (mode == kNintendoMode1) {
403 new_piece->argument[1] =
405 new_piece->argument[0] = (offset + kMaxLengthCompression) >> 8;
406 }
407 } break;
408 default: {
409 return absl::InvalidArgumentError(
410 "SplitCompressionCommand: Invalid Command");
411 }
412 }
413 return new_piece;
414}
415
417 int mode) {
418 uint pos = 0;
419 auto piece = start;
420 std::vector<uint8_t> output;
421
422 while (piece != nullptr) {
423 if (piece->length <= kMaxLengthNormalHeader) { // Normal header
424 output.push_back(BUILD_HEADER(piece->command, piece->length));
425 pos++;
426 } else {
427 if (piece->length <= kMaxLengthCompression) {
428 output.push_back(kCompressionStringMod | ((uchar)piece->command << 2) |
429 (((piece->length - 1) & 0xFF00) >> 8));
430 pos++;
431 printf("Building extended header : cmd: %d, length: %d - %02X\n",
432 piece->command, piece->length, output[pos - 1]);
433 output.push_back(((piece->length - 1) & 0x00FF)); // (char)
434 pos++;
435 } else {
436 // We need to split the command
437 auto new_piece = SplitCompressionPiece(piece, mode);
438 if (!new_piece.ok()) {
439 std::cout << new_piece.status().ToString() << std::endl;
440 }
441 printf("New added piece\n");
442 auto piece_data = new_piece.value();
443 PrintCompressionPiece(piece_data);
444 piece_data->next = piece->next;
445 piece->next = piece_data;
446 continue;
447 }
448 }
449
450 if (piece->command == kCommandRepeatingBytes) {
451 char tmp[2];
452 tmp[0] = piece->argument[0];
453 tmp[1] = piece->argument[1];
454 if (mode == kNintendoMode1) {
455 tmp[0] = piece->argument[1];
456 tmp[1] = piece->argument[0];
457 }
458 for (const auto& each : tmp) {
459 output.push_back(each);
460 pos++;
461 }
462 } else {
463 for (int i = 0; i < piece->argument_length; ++i) {
464 output.push_back(piece->argument[i]);
465 pos++;
466 }
467 }
468 pos += piece->argument_length;
469 piece = piece->next;
470 }
471 output.push_back(kSnesByteMax);
472 return output;
473}
474
476 int mode, int start, int src_data_pos) {
477 if (chain_head->next != nullptr) {
478 Rom temp_rom;
480 temp_rom.LoadFromBytes(CreateCompressionString(chain_head->next, mode)))
481 ASSIGN_OR_RETURN(auto decomp_data,
482 DecompressV2(temp_rom.data(), 0, temp_rom.size()))
483 if (!std::equal(decomp_data.begin() + start, decomp_data.end(),
484 temp_rom.begin())) {
485 return absl::InternalError(absl::StrFormat(
486 "Compressed data does not match uncompressed data at %d\n",
487 (uint)(src_data_pos - start)));
488 }
489 }
490 return absl::OkStatus();
491}
492
493// Merge consecutive copy if possible
495 CompressionPiecePointer piece = start;
496
497 while (piece != nullptr) {
498 if (piece->command == kCommandDirectCopy && piece->next != nullptr &&
499 piece->next->command == kCommandDirectCopy &&
500 piece->length + piece->next->length <= kMaxLengthCompression) {
501 uint previous_length = piece->length;
502 piece->length = piece->length + piece->next->length;
503
504 for (int i = 0; i < piece->next->argument_length; ++i) {
505 piece->argument[i + previous_length] = piece->next->argument[i];
506 }
507 piece->argument_length = piece->length;
509
510 auto p_next_next = piece->next->next;
511 piece->next = p_next_next;
512 continue; // Next could be another copy
513 }
514 piece = piece->next;
515 }
516 return start;
517}
518
519absl::StatusOr<std::vector<uint8_t>> CompressV2(const uchar* data,
520 const int start,
521 const int length, int mode,
522 bool check) {
523 // Surely there's no need to compress zero...
524 if (length == 0) {
525 return std::vector<uint8_t>();
526 }
527
528 // Worst case should be a copy of the string with extended header
529 auto compressed_chain = std::make_shared<CompressionPiece>(1, 1, "aaa", 2);
530 auto compressed_chain_start = compressed_chain;
531
532 CompressionCommand current_cmd = {/*argument*/ {{}},
533 /*cmd_size*/ {0, 1, 2, 1, 2},
534 /*data_size*/ {0, 0, 0, 0, 0}};
535
536 uint src_pos = start;
537 uint last_pos = start + length - 1;
538 uint comp_accumulator = 0; // Used when skipping using copy
539
540 while (true) {
541 current_cmd.data_size.fill({});
542 current_cmd.arguments.fill({{}});
543
544 CheckByteRepeatV2(data, src_pos, last_pos, current_cmd);
545 CheckWordRepeatV2(data, src_pos, last_pos, current_cmd);
546 CheckIncByteV2(data, src_pos, last_pos, current_cmd);
547 CheckIntraCopyV2(data, src_pos, last_pos, start, current_cmd);
548
549 uint max_win = 2;
550 uint cmd_with_max = kCommandDirectCopy;
551 ValidateForByteGain(current_cmd.data_size, current_cmd.cmd_size, max_win,
552 cmd_with_max);
553 // ValidateForByteGainV2(current_cmd, max_win, cmd_with_max);
554
555 if (cmd_with_max == kCommandDirectCopy) {
556 // This is the worst case scenario
557 // Progress through the next byte, in case there's a different
558 // compression command we can implement before we hit 32 bytes.
559 src_pos++;
560 comp_accumulator++;
561
562 // Arbitrary choice to do a 32 bytes grouping for copy.
563 if (comp_accumulator == 32 || src_pos > last_pos) {
564 std::string buffer = SetBuffer(data, src_pos, comp_accumulator);
565 auto new_comp_piece = std::make_shared<CompressionPiece>(
566 kCommandDirectCopy, comp_accumulator, buffer, comp_accumulator);
567 compressed_chain->next = new_comp_piece;
568 comp_accumulator = 0;
569 }
570 } else {
571 AddAlternativeCompressionCommand(data, compressed_chain, current_cmd,
572 src_pos, comp_accumulator, cmd_with_max,
573 max_win);
574 }
575
576 if (src_pos > last_pos) {
577 printf("Breaking compression loop\n");
578 break;
579 }
580
581 if (check) {
582 RETURN_IF_ERROR(ValidateCompressionResult(compressed_chain_start, mode,
583 start, src_pos))
584 }
585 }
586
587 // Skipping compression chain header
588 MergeCopy(compressed_chain_start->next);
589 PrintCompressionChain(compressed_chain_start);
590 return CreateCompressionString(compressed_chain_start->next, mode);
591}
592
593// Hyrule Magic
594uint8_t* Compress(uint8_t const* const src, int const oldsize, int* const size,
595 int const flag) {
596 unsigned char* b2 =
597 (unsigned char*)malloc(0x1000); // allocate a 2^12 sized buffer
598
599 int i, j, k, l, m = 0, n, o = 0, bd = 0, p, q = 0, r;
600
601 for (i = 0; i < oldsize;) {
602 l = src[i]; // grab a char from the buffer.
603
604 k = 0;
605
606 r = !!q; // r = the same logical value (0 or 1) as q, but not the same
607 // value necesarily.
608
609 for (j = 0; j < i - 1; j++) {
610 if (src[j] == l) {
611 m = oldsize - j;
612
613 for (n = 0; n < m; n++)
614 if (src[n + j] != src[n + i]) break;
615
616 if (n > k) k = n, o = j;
617 }
618 }
619
620 for (n = i + 1; n < oldsize; n++) {
621 if (src[n] != l) {
622 // look for chars identical to the first one.
623 // stop if we can't find one.
624 // n will reach i+k+1 for some k >= 0.
625
626 break;
627 }
628 }
629
630 n -= i; // offset back by i. i.e. n = k+1 as above.
631
632 if (n > 1 + r)
633 p = 1;
634 else {
635 m = src[i + 1];
636
637 for (n = i + 2; n < oldsize; n++) {
638 if (src[n] != l) break;
639
640 n++;
641
642 if (src[n] != m) break;
643 }
644
645 n -= i;
646
647 if (n > 2 + r)
648 p = 2;
649 else {
650 m = oldsize - i;
651
652 for (n = 1; n < m; n++)
653 if (src[i + n] != l + n) break;
654
655 if (n > 1 + r)
656 p = 3;
657 else
658 p = 0;
659 }
660 }
661
662 if (k > 3 + r && k > n + (p & 1)) p = 4, n = k;
663
664 if (!p)
665 q++, i++;
666 else {
667 if (q) {
668 q--;
669
670 if (q > 31) {
671 b2[bd++] = (unsigned char)(224 + (q >> 8));
672 }
673
674 b2[bd++] = (unsigned char)q;
675 q++;
676
677 memcpy(b2 + bd, src + i - q, q);
678
679 bd += q;
680 q = 0;
681 }
682
683 i += n;
684 n--;
685
686 if (n > 31) {
687 b2[bd++] = (unsigned char)(224 + (n >> 8) + (p << 2));
688 b2[bd++] = (unsigned char)n;
689 } else
690 b2[bd++] = (unsigned char)((p << 5) + n);
691
692 switch (p) {
693 case 1:
694 case 3:
695 b2[bd++] = (unsigned char)l;
696 break;
697
698 case 2:
699 b2[bd++] = (unsigned char)l;
700 b2[bd++] = (unsigned char)m;
701
702 break;
703
704 case 4:
705 if (flag) {
706 b2[bd++] = (unsigned char)(o >> 8);
707 b2[bd++] = (unsigned char)o;
708 } else {
709 b2[bd++] = (unsigned char)o;
710 b2[bd++] = (unsigned char)(o >> 8);
711 }
712 }
713
714 continue;
715 }
716 }
717
718 if (q) {
719 q--;
720
721 if (q > 31) {
722 b2[bd++] = (unsigned char)(224 + (q >> 8));
723 }
724
725 b2[bd++] = (unsigned char)q;
726 q++;
727
728 memcpy(b2 + bd, src + i - q, q);
729
730 bd += q;
731 }
732
733 b2[bd++] = 255;
734 b2 = (unsigned char*)realloc(b2, bd);
735 *size = bd;
736
737 return b2;
738}
739
740uint8_t* Uncompress(uint8_t const* src, int* const size,
741 int const p_big_endian) {
742 unsigned char* b2 = (unsigned char*)malloc(1024);
743
744 int bd = 0, bs = 1024;
745
746 unsigned char a;
747 unsigned char b;
748 unsigned short c, d;
749
750 for (;;) {
751 // retrieve a uchar from the buffer.
752 a = *(src++);
753
754 // end the decompression routine if we encounter 0xff.
755 if (a == 0xff) break;
756
757 // examine the top 3 bits of a.
758 b = (a >> 5);
759
760 if (b == 7) // i.e. 0b 111
761 {
762 // get bits 0b 0001 1100
763 b = ((a >> 2) & 7);
764
765 // get bits 0b 0000 0011, multiply by 256, OR with the next byte.
766 c = ((a & 0x0003) << 8);
767 c |= *(src++);
768 } else
769 // or get bits 0b 0001 1111
770 c = (uint16_t)(a & 31);
771
772 c++;
773
774 if ((bd + c) > (bs - 512)) {
775 // need to increase the buffer size.
776 bs += 1024;
777 b2 = (uint8_t*)realloc(b2, bs);
778 }
779
780 // 7 was handled, here we handle other decompression codes.
781
782 switch (b) {
783 case 0: // 0b 000
784
785 // raw copy
786
787 // copy info from the src buffer to our new buffer,
788 // at offset bd (which we'll be increasing;
789 memcpy(b2 + bd, src, c);
790
791 // increment the src pointer accordingly.
792 src += c;
793
794 bd += c;
795
796 break;
797
798 case 1: // 0b 001
799
800 // rle copy
801
802 // make c duplicates of one byte, inc the src pointer.
803 memset(b2 + bd, *(src++), c);
804
805 // increase the b2 offset.
806 bd += c;
807
808 break;
809
810 case 2: // 0b 010
811
812 // rle 16-bit alternating copy
813
814 d = core::ldle16b(src);
815
816 src += 2;
817
818 while (c > 1) {
819 // copy that 16-bit number c/2 times into the b2 buffer.
820 core::stle16b(b2 + bd, d);
821
822 bd += 2;
823 c -= 2; // hence c/2
824 }
825
826 if (c) // if there's a remainder of c/2, this handles it.
827 b2[bd++] = (char)d;
828
829 break;
830
831 case 3: // 0b 011
832
833 // incrementing copy
834
835 // get the current src byte.
836 a = *(src++);
837
838 while (c--) {
839 // increment that byte and copy to b2 in c iterations.
840 // e.g. a = 4, b2 will have 4,5,6,7,8... written to it.
841 b2[bd++] = a++;
842 }
843
844 break;
845
846 default: // 0b 100, 101, 110
847
848 // lz copy
849
850 if (p_big_endian) {
851 d = (*src << 8) + src[1];
852 } else {
853 d = core::ldle16b(src);
854 }
855
856 while (c--) {
857 // copy from a different location in the buffer.
858 b2[bd++] = b2[d++];
859 }
860
861 src += 2;
862 }
863 }
864
865 b2 = (unsigned char*)realloc(b2, bd);
866
867 if (size) (*size) = bd;
868
869 // return the unsigned char* buffer b2, which contains the uncompressed data.
870 return b2;
871}
872
873absl::StatusOr<std::vector<uint8_t>> CompressGraphics(const uchar* data,
874 const int pos,
875 const int length) {
876 return CompressV2(data, pos, length, kNintendoMode2);
877}
878
879absl::StatusOr<std::vector<uint8_t>> CompressOverworld(const uchar* data,
880 const int pos,
881 const int length) {
882 return CompressV2(data, pos, length, kNintendoMode1);
883}
884
885absl::StatusOr<std::vector<uint8_t>> CompressOverworld(
886 const std::vector<uint8_t> data, const int pos, const int length) {
887 return CompressV3(data, pos, length, kNintendoMode1);
888}
889
890// ============================================================================
891// Compression V3
892
894 uint 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 = "
909 << context.current_cmd.data_size[kCommandByteFill]);
910 }
911}
912
914 if (context.src_pos + 1 <= context.last_pos) { // Changed the condition here
915 uint 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 uint 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 = "
966 << (int)context.current_cmd.arguments[kCommandIncreasingFill][0]
967 << ", size = "
968 << context.current_cmd.data_size[kCommandIncreasingFill]);
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 uint max_copied_size = 0;
980 uint 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 uint 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 >
1010 context.current_cmd.data_size[kCommandRepeatingBytes]) {
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 (uint cmd_i = 1; cmd_i < 5; cmd_i++) {
1053 uint 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;
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 uint 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 new_piece =
1215 CompressionPiece(piece.command, length_left, nullptr, length_left);
1216 // MEMCPY
1217 for (int i = 0; i < length_left; ++i) {
1218 new_piece.argument[i] = piece.argument[i + kMaxLengthCompression];
1219 }
1220 break;
1223 uint offset = piece.argument[0] + (piece.argument[1] << 8);
1224 new_piece = CompressionPiece(piece.command, length_left, piece.argument,
1225 piece.argument_length);
1226 if (mode == kNintendoMode2) {
1227 new_piece.argument[0] = (offset + kMaxLengthCompression) & kSnesByteMax;
1228 new_piece.argument[1] = (offset + kMaxLengthCompression) >> 8;
1229 }
1230 if (mode == kNintendoMode1) {
1231 new_piece.argument[1] = (offset + kMaxLengthCompression) & kSnesByteMax;
1232 new_piece.argument[0] = (offset + kMaxLengthCompression) >> 8;
1233 }
1234 } break;
1235 default: {
1236 return absl::InvalidArgumentError(
1237 "SplitCompressionCommand: Invalid Command");
1238 }
1239 }
1240
1241 return new_piece;
1242}
1243
1245 uint pos = 0;
1246
1247 for (CompressionPiece& piece : context.compression_pieces) {
1248 if (piece.length <= kMaxLengthNormalHeader) { // Normal Header
1249 context.compression_string.push_back(
1250 BUILD_HEADER(piece.command, piece.length));
1251 pos++;
1252 } else {
1253 if (piece.length <= kMaxLengthCompression) {
1254 context.compression_string.push_back(
1255 kCompressionStringMod | ((uchar)piece.command << 2) |
1256 (((piece.length - 1) & 0xFF00) >> 8));
1257 pos++;
1258 std::cout << "Building extended header : cmd: " << piece.command
1259 << ", length: " << piece.length << " - "
1260 << (int)context.compression_string[pos - 1] << std::endl;
1261 context.compression_string.push_back(
1262 ((piece.length - 1) & 0x00FF)); // (char)
1263 } else {
1264 // We need to split the command
1265 auto new_piece = SplitCompressionPieceV3(piece, context.mode);
1266 if (!new_piece.ok()) {
1267 std::cout << new_piece.status().ToString() << std::endl;
1268 }
1269 context.compression_pieces.insert(
1270 context.compression_pieces.begin() + pos + 1, new_piece.value());
1271 continue;
1272 }
1273 }
1274
1275 if (piece.command == kCommandRepeatingBytes) {
1276 char tmp[2];
1277 tmp[0] = piece.argument[0];
1278 tmp[1] = piece.argument[1];
1279 if (context.mode == kNintendoMode1) {
1280 tmp[0] = piece.argument[1];
1281 tmp[1] = piece.argument[0];
1282 }
1283 for (const auto& each : tmp) {
1284 context.compression_string.push_back(each);
1285 pos++;
1286 }
1287 } else {
1288 for (int i = 0; i < piece.argument_length; ++i) {
1289 context.compression_string.push_back(piece.argument[i]);
1290 pos++;
1291 }
1292 }
1293 pos += piece.argument_length;
1294 }
1295
1296 // Add any remaining uncompressed data
1297 if (context.comp_accumulator > 0) {
1298 context.compressed_data.insert(
1299 context.compressed_data.end(),
1300 context.data.begin() + context.src_pos - context.comp_accumulator,
1301 context.data.begin() + context.src_pos);
1302 context.comp_accumulator = 0;
1303 }
1304
1305 // Add the end marker to the compressed data
1306 context.compressed_data.push_back(kSnesByteMax);
1307 DEBUG_LOG("FinalizeCompression: compressed_data size = "
1308 << context.compressed_data.size());
1309}
1310
1311absl::StatusOr<std::vector<uint8_t>> CompressV3(
1312 const std::vector<uint8_t>& data, const int start, const int length,
1313 int mode, bool check) {
1314 if (length == 0) {
1315 return std::vector<uint8_t>();
1316 }
1317
1318 CompressionContext context(data, start, length, mode);
1319 InitializeCompression(context);
1320
1321 while (context.src_pos <= context.last_pos) {
1323 DetermineBestCompression(context);
1324
1325 DEBUG_LOG("CompressV3 Loop: cmd_with_max = " << context.cmd_with_max);
1326
1327 if (context.cmd_with_max == kCommandDirectCopy) {
1328 HandleDirectCopy(context);
1329 } else {
1330 AddCompressionToChain(context);
1331 }
1332
1333 if (check) {
1335 }
1336 }
1337
1338 FinalizeCompression(context);
1339 return std::vector<uint8_t>(context.compressed_data.begin(),
1340 context.compressed_data.end());
1341}
1342
1343// Decompression
1344
1345std::string SetBuffer(const uchar* 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 uchar* 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 uchar* data, int offset,
1373 int size, int mode) {
1374 if (size == 0) {
1375 return std::vector<uint8_t>();
1376 }
1377
1378 std::vector<uint8_t> buffer(size, 0);
1379 uint length = 0;
1380 uint buffer_pos = 0;
1381 uchar command = 0;
1382 uchar header = data[offset];
1383
1384 while (header != kSnesByteMax) {
1385 if ((header & kExpandedMod) == kExpandedMod) {
1386 // Expanded Command
1387 command = ((header >> 2) & kCommandMod);
1388 length = (((header << 8) | data[offset + 1]) & kExpandedLengthMod);
1389 offset += 2; // Advance 2 bytes in ROM
1390 } else {
1391 // Normal Command
1392 command = ((header >> 5) & kCommandMod);
1393 length = (header & kNormalLengthMod);
1394 offset += 1; // Advance 1 byte in ROM
1395 }
1396 length += 1; // each commands is at least of size 1 even if index 00
1397
1398 switch (command) {
1399 case kCommandDirectCopy: // Does not advance in the ROM
1400 memcpy(buffer.data() + buffer_pos, data + offset, length);
1401 buffer_pos += length;
1402 offset += length;
1403 break;
1404 case kCommandByteFill:
1405 memset(buffer.data() + buffer_pos, (int)(data[offset]), length);
1406 buffer_pos += length;
1407 offset += 1; // Advances 1 byte in the ROM
1408 break;
1409 case kCommandWordFill:
1410 memfill(data, buffer, buffer_pos, offset, length);
1411 buffer_pos += length;
1412 offset += 2; // Advance 2 byte in the ROM
1413 break;
1415 auto inc_byte = data[offset];
1416 for (int i = 0; i < length; i++) {
1417 buffer[buffer_pos] = inc_byte++;
1418 buffer_pos++;
1419 }
1420 offset += 1; // Advance 1 byte in the ROM
1421 } break;
1423 ushort s1 = ((data[offset + 1] & kSnesByteMax) << 8);
1424 ushort s2 = (data[offset] & kSnesByteMax);
1425 int addr = (s1 | s2);
1426 if (mode == kNintendoMode1) { // Reversed byte order for
1427 // overworld maps
1428 addr = (data[offset + 1] & kSnesByteMax) |
1429 ((data[offset] & kSnesByteMax) << 8);
1430 }
1431 if (addr > offset) {
1432 return absl::InternalError(
1433 absl::StrFormat("Decompress: Offset for command copy exceeds "
1434 "current position "
1435 "(Offset : %#04x | Pos : %#06x)\n",
1436 addr, offset));
1437 }
1438 if (buffer_pos + length >= size) {
1439 size *= 2;
1440 buffer.resize(size);
1441 }
1442 memcpy(buffer.data() + buffer_pos, buffer.data() + addr, length);
1443 buffer_pos += length;
1444 offset += 2;
1445 } break;
1446 default: {
1447 std::cout << absl::StrFormat(
1448 "Decompress: Invalid header (Offset : %#06x, Command: %#04x)\n",
1449 offset, command);
1450 } break;
1451 }
1452 // check next byte
1453 header = data[offset];
1454 }
1455
1456 return buffer;
1457}
1458
1459absl::StatusOr<std::vector<uint8_t>> DecompressGraphics(const uchar* data,
1460 int pos, int size) {
1461 return DecompressV2(data, pos, size, kNintendoMode2);
1462}
1463
1464absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(const uchar* data,
1465 int pos, int size) {
1466 return DecompressV2(data, pos, size, kNintendoMode1);
1467}
1468
1469absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(
1470 const std::vector<uint8_t> data, int pos, int size) {
1471 return DecompressV2(data.data(), pos, size, kNintendoMode1);
1472}
1473
1474} // namespace lc_lz2
1475} // namespace gfx
1476} // namespace app
1477} // namespace yaze
The Rom class is used to load, save, and modify Rom data.
Definition rom.h:145
auto begin()
Definition rom.h:468
auto size() const
Definition rom.h:467
auto data()
Definition rom.h:470
absl::Status LoadFromBytes(const std::vector< uint8_t > &data)
Definition rom.cc:243
#define DEBUG_LOG(msg)
#define BUILD_HEADER(command, length)
Definition compression.h:12
unsigned int uint
Definition constants.h:120
#define RETURN_IF_ERROR(expression)
Definition constants.h:69
#define ASSIGN_OR_RETURN(type_variable_name, expression)
Definition constants.h:77
unsigned char uchar
Definition constants.h:121
unsigned short ushort
Definition constants.h:119
void stle16b(uint8_t *const p_arr, uint16_t const p_val)
Definition common.cc:118
uint16_t ldle16b(uint8_t const *const p_arr)
Definition common.cc:123
constexpr int kCommandMod
Definition compression.h:49
constexpr int kExpandedMod
Definition compression.h:50
void DetermineBestCompression(CompressionContext &context)
std::vector< uint8_t > CreateCompressionString(CompressionPiecePointer &start, int mode)
absl::StatusOr< std::vector< uint8_t > > CompressOverworld(const uchar *data, const int pos, const int length)
void ValidateForByteGain(const DataSizeArray &data_size_taken, const CommandSizeArray &cmd_size, uint &max_win, uint &cmd_with_max)
constexpr int kNintendoMode2
Definition compression.h:47
constexpr int kSnesByteMax
Definition compression.h:48
void CheckIntraCopyV2(const uchar *rom_data, uint &src_data_pos, const uint last_pos, uint start, CompressionCommand &cmd)
constexpr int kCommandByteFill
Definition compression.h:39
constexpr int kNormalLengthMod
Definition compression.h:52
constexpr int kCompressionStringMod
Definition compression.h:53
absl::StatusOr< std::vector< uint8_t > > DecompressGraphics(const uchar *data, int pos, int size)
void CheckIntraCopy(const uchar *rom_data, DataSizeArray &data_size_taken, CommandArgumentArray &cmd_args, uint &src_data_pos, const uint last_pos, uint start)
void CheckWordRepeatV3(CompressionContext &context)
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.
void CheckIncByteV3(CompressionContext &context)
void CheckIncByteV2(const uchar *rom_data, uint &src_data_pos, const uint last_pos, CompressionCommand &cmd)
absl::StatusOr< std::vector< uint8_t > > DecompressOverworld(const uchar *data, int pos, int size)
void AddCompressionToChain(CompressionContext &context)
void PrintCompressionPiece(const CompressionPiecePointer &piece)
void InitializeCompression(CompressionContext &context)
constexpr int kCommandIncreasingFill
Definition compression.h:41
constexpr int kCommandDirectCopy
Definition compression.h:38
std::array< uint, 5 > CommandSizeArray
Definition compression.h:68
constexpr int kMaxLengthNormalHeader
Definition compression.h:44
void CheckIntraCopyV3(CompressionContext &context)
void CheckWordRepeat(const uchar *rom_data, DataSizeArray &data_size_taken, CommandArgumentArray &cmd_args, uint &src_data_pos, const uint last_pos)
absl::Status ValidateCompressionResult(CompressionPiecePointer &chain_head, int mode, int start, int src_data_pos)
void AddAlternativeCompressionCommand(const uchar *rom_data, CompressionPiecePointer &compressed_chain, const CompressionCommand &command, uint &source_data_position, uint &uncompressed_data_size, uint &best_command, uint &best_command_gain)
absl::StatusOr< std::vector< uint8_t > > CompressV2(const uchar *data, const int start, const int length, int mode, bool check)
Compresses a buffer of data using the LC_LZ2 algorithm.
void memfill(const uchar *data, std::vector< uint8_t > &buffer, int buffer_pos, int offset, int length)
CompressionPiecePointer MergeCopy(CompressionPiecePointer &start)
std::array< std::array< char, 2 >, 5 > CommandArgumentArray
Definition compression.h:67
absl::StatusOr< std::vector< uint8_t > > DecompressV2(const uchar *data, int offset, int size, int mode)
Decompresses a buffer of data using the LC_LZ2 algorithm.
void CheckByteRepeat(const uchar *rom_data, DataSizeArray &data_size_taken, CommandArgumentArray &cmd_args, uint &src_data_pos, const uint last_pos)
absl::StatusOr< CompressionPiecePointer > SplitCompressionPiece(CompressionPiecePointer &piece, int mode)
void CheckWordRepeatV2(const uchar *data, uint &src_pos, const uint last_pos, CompressionCommand &cmd)
constexpr int kNintendoMode1
Definition compression.h:46
constexpr int kCommandRepeatingBytes
Definition compression.h:42
constexpr int kExpandedLengthMod
Definition compression.h:51
uint8_t * Uncompress(uint8_t const *src, int *const size, int const p_big_endian)
constexpr int kCommandWordFill
Definition compression.h:40
void CheckAvailableCompressionCommands(CompressionContext &context)
uint8_t * Compress(uint8_t const *const src, int const oldsize, int *const size, int const flag)
constexpr int kMaxLengthCompression
Definition compression.h:45
void CheckByteRepeatV2(const uchar *data, uint &src_pos, const uint last_pos, CompressionCommand &cmd)
void CompressionCommandAlternativeV2(const uchar *rom_data, const CompressionCommand &cmd, CompressionPiecePointer &compressed_chain, uint &src_data_pos, uint &comp_accumulator, uint &cmd_with_max, uint &max_win)
std::array< uint, 5 > DataSizeArray
Definition compression.h:69
absl::StatusOr< CompressionPiece > SplitCompressionPieceV3(CompressionPiece &piece, int mode)
void ValidateForByteGainV2(const CompressionCommand &cmd, uint &max_win, uint &cmd_with_max)
void CheckIncByte(const uchar *rom_data, DataSizeArray &data_size_taken, CommandArgumentArray &cmd_args, uint &src_data_pos, const uint last_pos)
absl::StatusOr< std::vector< uint8_t > > CompressGraphics(const uchar *data, const int pos, const int length)
void HandleDirectCopy(CompressionContext &context)
std::string SetBuffer(const uchar *data, int src_pos, int comp_accumulator)
const std::array< int, 5 > kCommandSizes
void CompressionCommandAlternative(const uchar *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)
void FinalizeCompression(CompressionContext &context)
void PrintCompressionChain(const CompressionPiecePointer &chain_head)
void CheckByteRepeatV3(CompressionContext &context)
std::shared_ptr< CompressionPiece > CompressionPiecePointer
Definition compression.h:83
struct CompressionPiece CompressionPiece
Definition compression.h:82
absl::Status ValidateCompressionResultV3(const CompressionContext &context)
Definition common.cc:21
std::array< std::array< char, 2 >, 5 > arguments
Definition compression.h:58
std::vector< CompressionPiece > compression_pieces