Rename Hyrule Magic compression functions; update deprecation notes

This commit is contained in:
scawful
2024-12-31 18:20:18 -05:00
parent fd102a5d3c
commit 308eb4b287
4 changed files with 323 additions and 325 deletions

View File

@@ -6,7 +6,6 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "app/core/constants.h"
#include "app/rom.h"
@@ -15,9 +14,287 @@
namespace yaze {
namespace gfx {
namespace lc_lz2 {
// Hyrule Magic
uint8_t* HyruleMagicCompress(uint8_t const* const src, int const oldsize,
int* const size, int const flag) {
unsigned char* b2 =
(unsigned char*)malloc(0x1000); // allocate a 2^12 sized buffer
// Compression commands
int i, j, k, l, m = 0, n, o = 0, bd = 0, p, q = 0, r;
for (i = 0; i < oldsize;) {
l = src[i]; // grab a char from the buffer.
k = 0;
r = !!q; // r = the same logical value (0 or 1) as q, but not the same
// value necesarily.
for (j = 0; j < i - 1; j++) {
if (src[j] == l) {
m = oldsize - j;
for (n = 0; n < m; n++)
if (src[n + j] != src[n + i]) break;
if (n > k) k = n, o = j;
}
}
for (n = i + 1; n < oldsize; n++) {
if (src[n] != l) {
// look for chars identical to the first one.
// stop if we can't find one.
// n will reach i+k+1 for some k >= 0.
break;
}
}
n -= i; // offset back by i. i.e. n = k+1 as above.
if (n > 1 + r)
p = 1;
else {
m = src[i + 1];
for (n = i + 2; n < oldsize; n++) {
if (src[n] != l) break;
n++;
if (src[n] != m) break;
}
n -= i;
if (n > 2 + r)
p = 2;
else {
m = oldsize - i;
for (n = 1; n < m; n++)
if (src[i + n] != l + n) break;
if (n > 1 + r)
p = 3;
else
p = 0;
}
}
if (k > 3 + r && k > n + (p & 1)) p = 4, n = k;
if (!p)
q++, i++;
else {
if (q) {
q--;
if (q > 31) {
b2[bd++] = (unsigned char)(224 + (q >> 8));
}
b2[bd++] = (unsigned char)q;
q++;
memcpy(b2 + bd, src + i - q, q);
bd += q;
q = 0;
}
i += n;
n--;
if (n > 31) {
b2[bd++] = (unsigned char)(224 + (n >> 8) + (p << 2));
b2[bd++] = (unsigned char)n;
} else
b2[bd++] = (unsigned char)((p << 5) + n);
switch (p) {
case 1:
case 3:
b2[bd++] = (unsigned char)l;
break;
case 2:
b2[bd++] = (unsigned char)l;
b2[bd++] = (unsigned char)m;
break;
case 4:
if (flag) {
b2[bd++] = (unsigned char)(o >> 8);
b2[bd++] = (unsigned char)o;
} else {
b2[bd++] = (unsigned char)o;
b2[bd++] = (unsigned char)(o >> 8);
}
}
continue;
}
}
if (q) {
q--;
if (q > 31) {
b2[bd++] = (unsigned char)(224 + (q >> 8));
}
b2[bd++] = (unsigned char)q;
q++;
memcpy(b2 + bd, src + i - q, q);
bd += q;
}
b2[bd++] = 255;
b2 = (unsigned char*)realloc(b2, bd);
*size = bd;
return b2;
}
uint8_t* HyruleMagicDecompress(uint8_t const* src, int* const size,
int const p_big_endian) {
unsigned char* b2 = (unsigned char*)malloc(1024);
int bd = 0, bs = 1024;
unsigned char a;
unsigned char b;
unsigned short c, d;
for (;;) {
// retrieve a uchar from the buffer.
a = *(src++);
// end the decompression routine if we encounter 0xff.
if (a == 0xff) break;
// examine the top 3 bits of a.
b = (a >> 5);
if (b == 7) // i.e. 0b 111
{
// get bits 0b 0001 1100
b = ((a >> 2) & 7);
// get bits 0b 0000 0011, multiply by 256, OR with the next byte.
c = ((a & 0x0003) << 8);
c |= *(src++);
} else
// or get bits 0b 0001 1111
c = (uint16_t)(a & 31);
c++;
if ((bd + c) > (bs - 512)) {
// need to increase the buffer size.
bs += 1024;
b2 = (uint8_t*)realloc(b2, bs);
}
// 7 was handled, here we handle other decompression codes.
switch (b) {
case 0: // 0b 000
// raw copy
// copy info from the src buffer to our new buffer,
// at offset bd (which we'll be increasing;
memcpy(b2 + bd, src, c);
// increment the src pointer accordingly.
src += c;
bd += c;
break;
case 1: // 0b 001
// rle copy
// make c duplicates of one byte, inc the src pointer.
memset(b2 + bd, *(src++), c);
// increase the b2 offset.
bd += c;
break;
case 2: // 0b 010
// rle 16-bit alternating copy
d = core::ldle16b(src);
src += 2;
while (c > 1) {
// copy that 16-bit number c/2 times into the b2 buffer.
core::stle16b(b2 + bd, d);
bd += 2;
c -= 2; // hence c/2
}
if (c) // if there's a remainder of c/2, this handles it.
b2[bd++] = (char)d;
break;
case 3: // 0b 011
// incrementing copy
// get the current src byte.
a = *(src++);
while (c--) {
// increment that byte and copy to b2 in c iterations.
// e.g. a = 4, b2 will have 4,5,6,7,8... written to it.
b2[bd++] = a++;
}
break;
default: // 0b 100, 101, 110
// lz copy
if (p_big_endian) {
d = (*src << 8) + src[1];
} else {
d = core::ldle16b(src);
}
while (c--) {
// copy from a different location in the buffer.
b2[bd++] = b2[d++];
}
src += 2;
}
}
b2 = (unsigned char*)realloc(b2, bd);
if (size) (*size) = bd;
// return the unsigned char* buffer b2, which contains the uncompressed data.
return b2;
}
namespace lc_lz2 {
void PrintCompressionPiece(const CompressionPiecePointer& piece) {
std::cout << "Command: " << std::to_string(piece->command) << "\n";
@@ -590,286 +867,6 @@ absl::StatusOr<std::vector<uint8_t>> CompressV2(const uchar* data,
return CreateCompressionString(compressed_chain_start->next, mode);
}
// Hyrule Magic
uint8_t* Compress(uint8_t const* const src, int const oldsize, int* const size,
int const flag) {
unsigned char* b2 =
(unsigned char*)malloc(0x1000); // allocate a 2^12 sized buffer
int i, j, k, l, m = 0, n, o = 0, bd = 0, p, q = 0, r;
for (i = 0; i < oldsize;) {
l = src[i]; // grab a char from the buffer.
k = 0;
r = !!q; // r = the same logical value (0 or 1) as q, but not the same
// value necesarily.
for (j = 0; j < i - 1; j++) {
if (src[j] == l) {
m = oldsize - j;
for (n = 0; n < m; n++)
if (src[n + j] != src[n + i]) break;
if (n > k) k = n, o = j;
}
}
for (n = i + 1; n < oldsize; n++) {
if (src[n] != l) {
// look for chars identical to the first one.
// stop if we can't find one.
// n will reach i+k+1 for some k >= 0.
break;
}
}
n -= i; // offset back by i. i.e. n = k+1 as above.
if (n > 1 + r)
p = 1;
else {
m = src[i + 1];
for (n = i + 2; n < oldsize; n++) {
if (src[n] != l) break;
n++;
if (src[n] != m) break;
}
n -= i;
if (n > 2 + r)
p = 2;
else {
m = oldsize - i;
for (n = 1; n < m; n++)
if (src[i + n] != l + n) break;
if (n > 1 + r)
p = 3;
else
p = 0;
}
}
if (k > 3 + r && k > n + (p & 1)) p = 4, n = k;
if (!p)
q++, i++;
else {
if (q) {
q--;
if (q > 31) {
b2[bd++] = (unsigned char)(224 + (q >> 8));
}
b2[bd++] = (unsigned char)q;
q++;
memcpy(b2 + bd, src + i - q, q);
bd += q;
q = 0;
}
i += n;
n--;
if (n > 31) {
b2[bd++] = (unsigned char)(224 + (n >> 8) + (p << 2));
b2[bd++] = (unsigned char)n;
} else
b2[bd++] = (unsigned char)((p << 5) + n);
switch (p) {
case 1:
case 3:
b2[bd++] = (unsigned char)l;
break;
case 2:
b2[bd++] = (unsigned char)l;
b2[bd++] = (unsigned char)m;
break;
case 4:
if (flag) {
b2[bd++] = (unsigned char)(o >> 8);
b2[bd++] = (unsigned char)o;
} else {
b2[bd++] = (unsigned char)o;
b2[bd++] = (unsigned char)(o >> 8);
}
}
continue;
}
}
if (q) {
q--;
if (q > 31) {
b2[bd++] = (unsigned char)(224 + (q >> 8));
}
b2[bd++] = (unsigned char)q;
q++;
memcpy(b2 + bd, src + i - q, q);
bd += q;
}
b2[bd++] = 255;
b2 = (unsigned char*)realloc(b2, bd);
*size = bd;
return b2;
}
uint8_t* Uncompress(uint8_t const* src, int* const size,
int const p_big_endian) {
unsigned char* b2 = (unsigned char*)malloc(1024);
int bd = 0, bs = 1024;
unsigned char a;
unsigned char b;
unsigned short c, d;
for (;;) {
// retrieve a uchar from the buffer.
a = *(src++);
// end the decompression routine if we encounter 0xff.
if (a == 0xff) break;
// examine the top 3 bits of a.
b = (a >> 5);
if (b == 7) // i.e. 0b 111
{
// get bits 0b 0001 1100
b = ((a >> 2) & 7);
// get bits 0b 0000 0011, multiply by 256, OR with the next byte.
c = ((a & 0x0003) << 8);
c |= *(src++);
} else
// or get bits 0b 0001 1111
c = (uint16_t)(a & 31);
c++;
if ((bd + c) > (bs - 512)) {
// need to increase the buffer size.
bs += 1024;
b2 = (uint8_t*)realloc(b2, bs);
}
// 7 was handled, here we handle other decompression codes.
switch (b) {
case 0: // 0b 000
// raw copy
// copy info from the src buffer to our new buffer,
// at offset bd (which we'll be increasing;
memcpy(b2 + bd, src, c);
// increment the src pointer accordingly.
src += c;
bd += c;
break;
case 1: // 0b 001
// rle copy
// make c duplicates of one byte, inc the src pointer.
memset(b2 + bd, *(src++), c);
// increase the b2 offset.
bd += c;
break;
case 2: // 0b 010
// rle 16-bit alternating copy
d = core::ldle16b(src);
src += 2;
while (c > 1) {
// copy that 16-bit number c/2 times into the b2 buffer.
core::stle16b(b2 + bd, d);
bd += 2;
c -= 2; // hence c/2
}
if (c) // if there's a remainder of c/2, this handles it.
b2[bd++] = (char)d;
break;
case 3: // 0b 011
// incrementing copy
// get the current src byte.
a = *(src++);
while (c--) {
// increment that byte and copy to b2 in c iterations.
// e.g. a = 4, b2 will have 4,5,6,7,8... written to it.
b2[bd++] = a++;
}
break;
default: // 0b 100, 101, 110
// lz copy
if (p_big_endian) {
d = (*src << 8) + src[1];
} else {
d = core::ldle16b(src);
}
while (c--) {
// copy from a different location in the buffer.
b2[bd++] = b2[d++];
}
src += 2;
}
}
b2 = (unsigned char*)realloc(b2, bd);
if (size) (*size) = bd;
// return the unsigned char* buffer b2, which contains the uncompressed data.
return b2;
}
absl::StatusOr<std::vector<uint8_t>> CompressGraphics(const uchar* data,
const int pos,
const int length) {
@@ -887,9 +884,6 @@ absl::StatusOr<std::vector<uint8_t>> CompressOverworld(
return CompressV3(data, pos, length, kNintendoMode1);
}
// ============================================================================
// Compression V3
void CheckByteRepeatV3(CompressionContext& context) {
uint pos = context.src_pos;

View File

@@ -13,6 +13,13 @@
namespace yaze {
namespace gfx {
// Hyrule Magic
uint8_t* HyruleMagicCompress(uint8_t const* const src, int const oldsize,
int* const size, int const flag);
uint8_t* HyruleMagicDecompress(uint8_t const* src, int* const size,
int const p_big_endian);
/**
* @namespace yaze::gfx::lc_lz2
* @brief Contains the LC_LZ2 compression algorithm.
@@ -138,31 +145,33 @@ void CompressionCommandAlternativeV2(const uchar* data,
/**
* @brief Compresses a buffer of data using the LC_LZ2 algorithm.
* \deprecated Use Compress and Uncompress instead.
* \deprecated Use HyruleMagicDecompress instead.
*/
absl::StatusOr<std::vector<uint8_t>> CompressV2(const uchar* data, const int start,
const int length, int mode = 1,
bool check = false);
absl::StatusOr<std::vector<uint8_t>> CompressV2(const uchar* data,
const int start,
const int length, int mode = 1,
bool check = false);
absl::StatusOr<std::vector<uint8_t>> CompressGraphics(const uchar* data, const int pos,
const int length);
absl::StatusOr<std::vector<uint8_t>> CompressOverworld(const uchar* data, const int pos,
const int length);
absl::StatusOr<std::vector<uint8_t>> CompressOverworld(const std::vector<uint8_t> data,
const int pos, const int length);
absl::StatusOr<std::vector<uint8_t>> CompressGraphics(const uchar* data,
const int pos,
const int length);
absl::StatusOr<std::vector<uint8_t>> CompressOverworld(const uchar* data,
const int pos,
const int length);
absl::StatusOr<std::vector<uint8_t>> CompressOverworld(
const std::vector<uint8_t> data, const int pos, const int length);
absl::StatusOr<CompressionPiecePointer> SplitCompressionPiece(
CompressionPiecePointer& piece, int mode);
std::vector<uint8_t> CreateCompressionString(CompressionPiecePointer& start, int mode);
std::vector<uint8_t> CreateCompressionString(CompressionPiecePointer& start,
int mode);
absl::Status ValidateCompressionResult(CompressionPiecePointer& chain_head,
int mode, int start, int src_data_pos);
CompressionPiecePointer MergeCopy(CompressionPiecePointer& start);
// Compression V3
struct CompressionContext {
std::vector<uint8_t> data;
std::vector<uint8_t> compressed_data;
@@ -209,42 +218,35 @@ void FinalizeCompression(CompressionContext& context);
/**
* @brief Compresses a buffer of data using the LC_LZ2 algorithm.
* \deprecated Use Compress and Uncompress instead.
* \deprecated Use HyruleMagicCompress
*/
absl::StatusOr<std::vector<uint8_t>> CompressV3(const std::vector<uint8_t>& data,
const int start, const int length,
int mode = 1, bool check = false);
// Hyrule Magic
uint8_t* Compress(uint8_t const* const src, int const oldsize, int* const size,
int const flag);
uint8_t* Uncompress(uint8_t const* src, int* const size,
int const p_big_endian);
// Decompression
absl::StatusOr<std::vector<uint8_t>> CompressV3(
const std::vector<uint8_t>& data, const int start, const int length,
int mode = 1, bool check = false);
std::string SetBuffer(const std::vector<uint8_t>& data, int src_pos,
int comp_accumulator);
std::string SetBuffer(const uchar* data, int src_pos, int comp_accumulator);
void memfill(const uchar* data, std::vector<uint8_t>& buffer, int buffer_pos, int offset,
int length);
void memfill(const uchar* data, std::vector<uint8_t>& buffer, int buffer_pos,
int offset, int length);
/**
* @brief Decompresses a buffer of data using the LC_LZ2 algorithm.
* \deprecated Use Compress and Uncompress instead.
* @note Works well for graphics but not overworld data. Prefer Hyrule Magic
* routines for overworld data.
*/
absl::StatusOr<std::vector<uint8_t>> DecompressV2(const uchar* data, int offset,
int size = 0x800, int mode = 1);
absl::StatusOr<std::vector<uint8_t>> DecompressGraphics(const uchar* data, int pos, int size);
absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(const uchar* data, int pos, int size);
absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(const std::vector<uint8_t> data,
int pos, int size);
int size = 0x800,
int mode = 1);
absl::StatusOr<std::vector<uint8_t>> DecompressGraphics(const uchar* data,
int pos, int size);
absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(const uchar* data,
int pos, int size);
absl::StatusOr<std::vector<uint8_t>> DecompressOverworld(
const std::vector<uint8_t> data, int pos, int size);
} // namespace lc_lz2
} // namespace gfx
} // namespace yaze
#endif // YAZE_APP_GFX_COMPRESSION_H

View File

@@ -150,7 +150,7 @@ absl::Status Rom::SaveAllGraphicsData() {
final_data = gfx::Bpp8SnesToIndexed(sheet_data, 8);
int size = 0;
if (compressed) {
auto compressed_data = gfx::lc_lz2::Compress(
auto compressed_data = gfx::HyruleMagicCompress(
final_data.data(), final_data.size(), &size, 1);
for (int j = 0; j < size; j++) {
sheet_data[j] = compressed_data[j];

View File

@@ -542,8 +542,10 @@ absl::Status Overworld::SaveOverworldMaps() {
std::vector<uint8_t> a, b;
int size_a, size_b;
// Compress single_map_1 and single_map_2
auto a_char = gfx::lc_lz2::Compress(single_map_1.data(), 256, &size_a, 1);
auto b_char = gfx::lc_lz2::Compress(single_map_2.data(), 256, &size_b, 1);
auto a_char =
gfx::HyruleMagicCompress(single_map_1.data(), 256, &size_a, 1);
auto b_char =
gfx::HyruleMagicCompress(single_map_2.data(), 256, &size_b, 1);
if (a_char == nullptr || b_char == nullptr) {
return absl::AbortedError("Error compressing map gfx.");
}